pandas.concatでcsvファイルを読み込み、連結する際に簡易的に整合性チェックを行う

@hurutoriya さんが、先日以下の記事を投稿していました。

shunyaueta.com

その後ツイッター上でやりとりしているうちにこんな話がありました。

当初は簡単にできるかなと思ったのですが、mergeならともかくconcatとなると、そんなに簡単にはいきません。

こうしたコードを使うケースというのは、大抵の場合探索的データ分析していたり、「素早く手軽に読み込みたい」というものなので、手軽さを失わないようにしながら最低限のチェックを行っていく必要があります。

連続したcsvを読み込むときにひっかかるケースの大きなものとしては、カラムの不一致とデータ型の不一致です。なので、この2つに絞ってバリデーションを行う、validate関数を作ってみました。

コードは長くなるので記事の末尾に載せています。

使い方は簡単で、まず、dfのリストの代わりに、 (pathlib.Path, df) のタプルのリストを作ります。

    data = [(path, pd.read_csv(str(path))) for path in pathlib.Path(f_path).glob('*.csv')]

あとはこれを validate(data) に入れて、 pd.concat に渡すだけです。

    pd.concat(validate(data))

もしカラムが一致していない場合は以下のようなエラーが出ます。

ValueError: ambiguous columns: file2.csv, file3.csv

もしカラムが一致していてもdtypeが一致していない場合は以下のようなエラーが出ます。

ValueError: inconsistent dtypes: int64, object in file2.csv, file4.csv


適当に作ったコードなのでエラー等あるかもしれません。
もし不具合等あったらお気軽にご報告ください。

コード全体(デモコードつき)

import pathlib
import typing

import pandas as pd

# data definition
## valid data
df1 = pd.DataFrame(
    [
        {"c1": 100, "c2": "a100"},
        {"c1": 101, "c2": "a101"},
    ]
)
## valid data
df2 = pd.DataFrame(
    [
        {"c1": 200, "c2": "a200"},
        {"c1": 202, "c2": "a202"},
    ]
)
## invalid data: ambiguous column names
df3 = pd.DataFrame(
    [
        {"c1": 300, "c3": "a300"},
        {"c1": 301, "c3": "a301"},
    ]
)
## invalid data: inconsistent dtypes

df4 = pd.DataFrame(
    [
        {"c1": "400", "c2": "a400"},
        {"c1": 401, "c2": "a401"},
    ]
)

## dataset test case 1: ambiguous column names
data1 = [
    (pathlib.Path("file1.csv"), df1),
    (pathlib.Path("file2.csv"), df2),
    (pathlib.Path("file3.csv"), df3),
]
## dataset test case 2: inconsistent dtypes
data2 = [
    (pathlib.Path("file1.csv"), df1),
    (pathlib.Path("file2.csv"), df2),
    (pathlib.Path("file4.csv"), df4),
]


def validate(
    data: typing.Sequence[typing.Tuple[pathlib.Path, typing.Sequence[pd.DataFrame]]]
) -> typing.Sequence[pd.DataFrame]:
    """simple data validation

    :param data: [(path, df)]
    :return: [df]
    """
    for x, y in zip(data, data[1:]):
        if x[1].columns.tolist() != y[1].columns.tolist():
            raise ValueError(f"ambiguous columns: {x[0]}, {y[0]}")
        for xd, yd in zip(x[1].dtypes, y[1].dtypes):
            if xd != yd:
                raise ValueError(f"inconsistent dtypes: {xd}, {yd} in {x[0]}, {y[0]}")
    return [x[1] for x in data]


print("### validate ambiguous columns demo ###")
try:
    pd.concat(validate(data1))
except ValueError as ve:
    print(ve)

print("### validate inconsistent dtypes demo ###")
try:
    pd.concat(validate(data2))
except ValueError as ve:
    print(ve)

謝辞

@aodag (zipのエレガントな書き方を教えてくれてありがとうございます)

Hadoop DistCp実践ガイド2020年版

Hadoop DistCp (distributed copy, でぃすとしーぴー、でぃすとこぴー) は、MapReduceを用いてHadoopクラスタ間でデータコピーするためのツールです。保守運用している場合を除き、おそらく2020年においても運用上の選択肢として残っている最後のMapReduceのツールです。この記事では、DistCpの紹介と実践的な使い方の基本について説明していきます。内容としては以下の通りです。

  • Distcpの概要と原理
  • 実践DistCp
    • DistCpにドライランはない
    • コピーとアップデートの挙動の違いを押さえる
    • スナップショットを取得する
    • ソースと宛先、どちらのクラスタでDistCpを実行するか
    • 異なるメジャーバージョン間でのデータ転送にwebhdfsを使う
    • -p オプションの挙動
    • 2つのコピー戦略: uniformizeとdynamic
    • map数の調整
    • 転送帯域

なんで今更DistCp?

DistCpの使い方についてきちんと書いているドキュメントがなかったので書きました。Hadoopのバイブルである象本さえ、DistCpについては本当に簡単なことしか書いておらず、実際の使い方についてまとめているドキュメントがありませんでした。Clouderaのようなベンダーの場合は Cloudera Manager という素晴らしいツールが持つデータレプリケーション機能に包含されていて、ユーザーはボタン一発でクラスタ間データ転送ができるため、DistCpについて細かい話を知る必要はありません。そこで、素のHadoopを使う人のためのDistCpの記事を書いておくことにしました。

DistCpについての機能一覧などの詳細については公式ドキュメントを参照してください。

Hadoop 第3版

Hadoop 第3版

  • 作者:Tom White
  • 発売日: 2013/07/26
  • メディア: 大型本

hadoop.apache.org

DistCpの概要

DistCp は、MapReduceを用いてHadoopクラスタ間で高速にデータコピーするためのツールで、Apache Hadoop の標準リリースに含まれています。Apache Hadoopは、分散ストレージのHDFS(Hadoop Distributed File System、Hadoop分散ファイルシステム)と、分散コンピューティングフレームワークのYARNから構成されている分散処理フレームワークで、MapReduceはYARN上で動く代表的なアプリケーションの一つです。 Hadoopクラスタ間と書きましたが、正確には分散ストレージ間と言った方が正しいでしょう。DistCpは、HDFSだけでなく、Amazon S3Azure Storage といったオブジェクトストレージにも対応しています。

DistCpはコマンドラインツールで、以下のような形式で実行します。

$ hadoop distcp hdfs://cluster1/foo/bar hdfs://cluster2/foo

これは、cluster1というHDFSクラスタの、 /foo/bar というパスを、cluster2 というHDFSクラスタの、 /foo というディレクトリにコピーする、というコマンドとなります。

DistCpの原理

DistCpは、MapReduceフレームワークで動作します。まず、MapReduceについて簡単におさらいします。MapReduceは、複数のノードで別個に計算処理を行うMap、特定のキーごとにデータを転送して集約するShuffle、集約されたデータに対し、Mapと同様、ノードごとに独立して処理を行うReduceという3つのフェーズで分散処理を行うフレームワークです。
以下の図は、MapReduceの処理の流れを表しています。

f:id:shiumachi:20200719122107p:plain

DistCpは、Map処理のみを使い、何も計算せず(恒等関数)、入力と出力を別のクラスタで行うという形でMapReduceを使用しています。

以下の図は、DistCpの処理の流れを表しています。

f:id:shiumachi:20200719122509p:plain

DistCpのソース(読み込み元)と宛先(書き込み先)はURIで表されます。先程の例では、宛先を hdfs://cluster2/foo としましたが、この宛先は s3a://bucket1/foo でも問題なく動作します。これは、S3上の bucket1 というバケットの配下にある foo という名前空間にデータをコピーすることを意味します。

実践DistCp: ドライランはない

DistCpは、非常に大規模かつ不可逆変更を行うツールであるにも関わらず、ドライランに相当する機能が存在しないという点に注意してください。ドライランがないということは、十分に検証クラスタでテストした後、本番での実行が成功することを、神(あるいはあなたが信仰する何か)に祈るしかなくなります。そして大抵の場合その祈りが届くことはありません。頑張りましょう。

ドライランについては6年間オープンしているJIRAがありますので、我こそはという方は実装お待ちしています。

issues.apache.org

実践DistCp: コピーとアップデートの挙動の違いを押さえる

hadoop distcp コマンドは、何もオプションをつけない場合は、コピーという挙動になります。これは、以下の操作を行います。

  • ソースにパスが存在し、宛先に存在しない場合はコピーする
  • ソースと宛先に同じパスが存在する場合は何もしない
  • ソースにパスが存在せず、宛先に存在する場合は何もしない

hadoop distcp -update では、以下のように挙動が変わります。

  • ソースにパスが存在し、宛先に存在しない場合はコピーする
  • ソースと宛先に同じパスが存在する場合、チェックサムなどコンテンツの中身を確認し、コンテンツが異なる場合はコピーする。コンテンツが同一の場合は何もしない
  • ソースにパスが存在せず、宛先に存在する場合は何もしない

hadoop distcp -update -delete では、以下のように挙動が変わります。

  • ソースにパスが存在し、宛先に存在しない場合はコピーする
  • ソースと宛先に同じパスが存在する場合、チェックサムなどコンテンツの中身を確認し、コンテンツが異なる場合はコピーする。コンテンツが同一の場合は何もしない
  • ソースにパスが存在せず、宛先に存在する場合はそのパスを削除する

これらの挙動をまとめると、以下の図のようになります。

f:id:shiumachi:20200719122945p:plain

hadoop distcp に -update をつける場合、コンテンツの中身を比較するため、オーバーヘッドが発生します。そのため、-updateなしに比べて処理性能が落ちることに注意してください。


DistCpのコピーとアップデートの挙動の違いは間違えやすく、そしてその間違いが重大な事故を起こしてしまう可能性がありますので絶対に覚えてください。

以下の2つの例を見てください。

# 例1
$ hadoop distcp hdfs://cluster1/foo/bar hdfs://cluster2/foo
# 例2: 誤った方法
$ hadoop distcp -update hdfs://cluster1/foo/bar hdfs://cluster2/foo

例1は、cluster2/foo の直下に cluster1/foo/bar をコピーするので、結果として cluster2/foo/bar が作成されます。
例2は、 cluster2/foo を cluster1/foo/bar の内容でアップデートするので、 cluster2/foo/bar は作成されず、cluster2/foo のコンテンツが cluster1/foo/bar と同じものになります。

図にすると以下のようになります。

f:id:shiumachi:20200719123329p:plain

この一例だけだとピンとこないかもしれませんので、もっと実務上実行する可能性のあるコマンドでみてみましょう。

# 例3
$ hadoop distcp hdfs://cluster1/user/sato hdfs://cluster2/user
# 例4: 誤った方法
$ hadoop distcp -update -delete hdfs://cluster1/user/sato hdfs://cluster2/user

hadoop distcp の -delete オプションは -update オプションと一緒に使わないと利用できないオプションで、ソースクラスタには存在しないけど宛先クラスタには存在する全てのパスを削除します。つまり、-delete を付与すると、ソースと宛先のコンテンツが全く同一のものとなります。
例3は、 cluster1/user/sato を、 cluster2/user/ にコピーします。よって、cluster2/user/sato が作成されます。
例4は、 cluster2/user のコンテンツが、cluster1/user/sato と全く同じものになります。つまり、 /user ディレクトリ配下に存在する全てのユーザーデータが完全に削除され、その代わりにユーザ sato のコンテンツだけが置かれるようになります。

図に表すと、以下のようになります。

f:id:shiumachi:20200719123553p:plain

「ゴミ箱機能があるから即座に削除されることはないのでは?」と思うかもしれませんが、DistCpのバグでゴミ箱は機能しません。この問題は2020/07/15現在未解決です。詳細については以下のJIRAも参照してください。

issues.apache.org

運用者はこのコマンドを誤って実行した時点で、即座に緊急事態のアラートを出さなければいけなくなるでしょう。

この例4は、正しくは以下のように書くべきでした。

# 例4: 誤った方法
$ hadoop distcp -update -delete hdfs://cluster1/user/sato hdfs://cluster2/user
# 例5: 例4の正しい書き方
$ hadoop distcp -update -delete hdfs://cluster1/user/sato hdfs://cluster2/user/sato

では、ここでもう一つの例を紹介しましょう。cluster2に既に/userが存在するときに、以下のコマンドを実行すると何が起きるでしょうか。

# 例6: 誤った方法
$ hadoop distcp hdfs://cluster1/user hdfs://cluster2/user

これが、-update ( -delete ) がついていたならば、問題なかったかもしれません。しかし、今回は -update がついていません。よって、 cluster1/user が cluster2/user の配下にコピーされます。つまり、 cluster2/user/user が作成されます。これは、多くの運用者にとって意図した挙動ではないでしょう。

このとき、安易に cluster2/user/user を削除することはできません。なぜなら、 cluster2/user/user というディレクトリはコピー前から存在していた可能性があり、その中にコンテンツが存在していた可能性があるからです。一度混じってしまえば、cluster1由来のコンテンツとcluster2オリジナルのコンテンツをふるい分けるのは困難でしょう。-update オプションがないときも決して油断してはいけません。
cluster1の/userをcluster2の/userにコピーする場合、以下のように書くべきでした。

# 例6: 誤った方法
$ hadoop distcp hdfs://cluster1/user hdfs://cluster2/user
# 例7: 例6の正しい書き方
$ hadoop distcp hdfs://cluster1/user hdfs://cluster2/

図に表すと、以下のようになります。

f:id:shiumachi:20200719124216p:plain

手動・自動での実行に関わらず、パスの確認は絶対に最後の最後まで確実に行うようにしてください。

実践DistCp: スナップショットを取得する

DistCpは、通常非常に膨大な時間がかかります。クラスタ全体のデータ転送の場合、1日や2日は当たり前で、1週間や1ヶ月に渡って転送し続ける、ということは頻繁に起こります。DistCpはMapReduce実行前に対象パスの一覧を取得しますので、転送中にソースファイルが変化しても一切考慮することはできません。大抵の場合、転送中にファイルが削除され、何日もかけたDistCpが失敗することになるでしょう。運良く転送に成功したとしても、コンテンツの中身に不整合が発生していれば、Hive等の別のアプリケーションでの処理結果が意図しないものとなり、いいことは一つもありません。そのため、ソースはスナップショットを指定するのが鉄則です。
スナップショットの取得は、以下の2つのコマンドを順番に実行します。

$ hdfs dfsadmin -allowSnapshot hdfs://cluster1/foo/bar
$ hdfs dfs -createSnapshot hdfs://cluster1/foo/bar snapshot1

hdfs dfsadmin -allowSnapshot は hdfs ユーザでないと実行できませんが、hdfs dfs -createSnapshot は、対象ディレクトリの権限を持っている一般ユーザでも実行可能です。上記コマンドを実行すると、 hdfs://cluster1/foo/bar/.snapshot/snapshot1 というディレクトリが作成され、この配下には hdfs://cluster1/foo/bar のコンテンツと全く同じハードリンクが作成されます。
snapshot1はスナップショット名なので、自由に変更してコマンドを実行してください。

スナップショットを使ったDistCpは以下のように記述します。

$ hadoop distcp hdfs://cluster1/foo/bar/.snapshot/snapshot1 hdfs://cluster2/foo

実践DistCp: ソースと宛先、どちらのクラスタでDistCpを実行するか

DistCpは、基本的には宛先クラスタ側で実行することを推奨します。DistCpを宛先クラスタ側で実行しなければならないケースとしては以下のようなものがあります。

  • 非セキュアクラスタからセキュアクラスタにデータをコピーする場合
  • 低いメジャーバージョンのクラスタから高いメジャーバージョンにデータをコピーする場合

また、新規クラスタへのデータ移行の場合、ソースクラスタは通常業務のアプリケーションが稼働している一方、宛先クラスタは大抵の場合本番稼働前なので、ソースクラスタの負荷を増やさずに、宛先のリソースを有効活用することができます。

DistCpをソースクラスタで実施しなければいけないケースもあります。例えば、セキュアクラスタから非セキュアクラスタへデータを転送する場合です。

Clouderaの以下のドキュメントの記載を引用します。

docs.cloudera.com

You can use DistCp and WebHDFS to copy data between a secure cluster and an insecure cluster. Note that when doing this, the distcp commands should be run from the secure cluster.

セキュアクラスタにおけるDistCpの方法についてはこの記事では扱いませんが、DistCpをどちらのクラスタで実施するかを検討する場合には頭の片隅にとどめておいてください。

実践DistCp: 異なるメジャーバージョン間でのデータ転送にwebhdfsを使う

webhdfsプロトコルを使うことで、メジャーバージョンの低いバージョンから高いバージョンへのデータ転送を行うことができます。

$ hadoop distcp webhdfs://cluster1/foo/bar hdfs://cluster2/foo

以下は参考リンクです。

docs.cloudera.com

実践DistCp: -p オプションの挙動

デフォルトでは、DistCpはファイル属性等はコピーしません。ファイル属性をコピーするには -p オプションを使いますが、このオプションの挙動には様々な制約事項が存在します。例えば、 -update オプションはコンテンツの中身が同一のパスに対してはコピーを実施しませんが、このときファイル属性だけが違っていてもその属性を更新したりはしません。
以下の例で、両クラスタに /foo/bar/file1 というファイルがあるとします。

$ hadoop distcp -update hdfs://cluster1/foo/bar hdfs://cluster2/foo

このとき、cluster1/foo/bar/file1 のパーミッションが644で、 cluster2/foo/bar/file1 のパーミッションが600となっていて、ファイルのコンテンツが全く同一である場合、cluster2/foo/bar/file1 のパーミッションは 600 のまま変更されません。

別の例を紹介しましょう。 -pt オプションを使うと更新日時などを保持できますが、このオプションは、NameNodeの設定の一つ、 dfs.namenode.accesstime.precision (デフォルト1時間) が0(無効)の場合利用できません。dfs.namenode.accesstime.precision を 0 にしたまま以下のコマンドを実行しても、失敗します。

$ hadoop distcp -pt hdfs://cluster1/foo/bar hdfs://cluster2/foo

このとき、以下のようなエラーが出力されます。

Error: org.apache.hadoop.ipc.RemoteException(java.io.IOException): Access time for hdfs is not configured. Please set dfs.namenode.accesstime.precision configuration parameter.

アクセス時間の設定はパフォーマンスの最適化のために0にするのが推奨で、Ambari / HDP はデフォルト0になっていますが、コミュニティ版もClouderaもデフォルト1時間なので、設定そのものを知らない人も多いと思います。 -p オプションを使うときはドキュメントを読むだけで設計せず、必ず検証をしてください。

実践DistCp: 2つのコピー戦略: uniformizeとdynamic

DistCpが各Mapタスクに処理対象のパスを振り分ける戦略は2タイプ存在します。デフォルトはuniformizeという、データサイズで分割する方法です。例えば転送対象のデータが100TBあり、mapタスクを1000で設定した場合、各mapタスクは100GBのデータを転送するように、ファイルパスを振り分けられます。この挙動は、ソースコードを読めばわかりますが、リストされたファイルを上から順に取り出していきサイズを足していき、転送対象の全データサイズ/map数を超えたら次のmapタスクに渡す、という操作を行っています。

github.com

理想的なHDFSの環境ではこれで問題ないのですが、小さいファイルが大量にある環境の場合は、uniformizeではうまくいきません。
uniformizeでは、どれだけたくさんのファイルがあっても、一定のサイズを超えない限りはそれらのファイルが1mapタスクに割り当てられてしまいます。割り当てられるファイルは、ファイルリストの上から順にファイルを取り出されます。ファイルリストは、単純に対象ディレクトリの配下のファイル・ディレクトリを再帰的にリストしているだけなので、同一ディレクトリのファイルは一箇所に固まっています。その結果、あるディレクトリのファイルは1タスクに集中することになります。
1ファイルに対するHDFSアクセスは非常に遅いです。環境にもよりますが、1ファイルあたり数msのオーダーは見たほうがいいでしょう。そのため、スモールファイルが多いストレージでは、データ転送速度は非常に遅くなります。そして、多くの場合、スモールファイルは局所化しています。これはすなわち、特定のディレクトリにスモールファイルが集中していることを意味します。
まとめると、特定のディレクトリに集中したスモールファイル群がまとめて1つのmapタスクに割り当てられる結果、mapタスクのスキューが発生し、そのmapタスクだけが極端に遅くなるという現象が発生します。

このような環境では、dynamic というもう一つのコピー戦略を使います。dynamic はファイル数でタスクあたりの割当を分割するオプションです。例えば、1億ファイルあるシステムで1000mapタスクで処理を分割する場合、1タスクあたり10万ファイルを担当することになります。
dynamicオプションを使う場合、uniformizeと逆に、極端にファイルサイズが大きいデータが集中しているケースに注意してください。ファイルサイズを考慮しないでデータを分割するため、特定のタスクだけ極端に大きなデータを処理しなければいけないというリスクが発生します。転送対象のデータ特性は必ず事前に調査しましょう。
dynamic 戦略を使うには、以下のようにオプションを与えます。

$ hadoop distcp -strategy dynamic hdfs://cluster1/foo/bar hdfs://cluster2/foo

実践DistCp: map数の調整

デフォルトではDistCpは20mapタスクしか使用しません。データ量やリソース状況に応じて、map数の調整をしたほうがいいでしょう。以下の例は、map数を100とする場合の例です。

$ hadoop distcp -m 100 hdfs://cluster1/foo/bar hdfs://cluster2/foo

map数の調整は、基本的なHadoopアプリケーションと同様、ストレージIOやリソースに応じて調整する必要があります。リソースをフルに使えるのであれば、総ディスク数の1~2倍くらいにしておくのがいいと思いますが、例えばスモールファイル中心のクラスタの場合IOよりもCPU依存になるはずなので、CPUコア数からタスク数を計算した方がいいかもしれませんし、クラスタのリソースが逼迫している状態であればむしろmap数を減らしてゆっくり処理した方がいいかもしれません。このあたりの計算に自信がなければ、まずはデフォルトで試験的に転送してみて、転送速度を計算した上で必要があればチューニングするという程度でいいと思います。

実践DistCp: 転送帯域

ネットワークの帯域リソースが逼迫している場合は、転送用の帯域を制御した方がいいでしょう。以下のように設定することで、1mapあたりの転送帯域を10MB/sに抑えることができます。

$ hadoop distcp -bandwidth 10 hdfs://cluster1/foo/bar hdfs://cluster2/foo

この記事で書いていないこと

ただコピーするといっても、細かい要件はプロジェクトによって異なり、それに応じてDistCpの様々な機能を活用していく必要があります。
この記事でカバーしていない内容は以下の通りです。

そして、クラスタ移行という話になったときは、必要な作業はDistCpだけではありません。例えば、HiveメタストアDBのデータ移行や、管理ツールのデータ移行など、考えるべき課題は他にもあります。これらについて、最新の情報をベースに体系的にまとめられた書籍は存在しないので、もし自信がないという場合はCloudera等のベンダーに相談することをおすすめします。

参考リンク

既出も含めて、参考リンクをまとめておきます。

謝辞

本記事の執筆にあたり、以下の方々にレビューしていただきました。この場を借りてお礼申し上げます。(順不同、敬称略)

自然言語処理ナイト #dllab

dllab.connpass.com

NLPに関するイベントとして目に入ってきたので参加してみました。

業界関係者でも自分がきちんと知っている分野でもなく、純粋に勉強目的で一参加者として勉強会に参加したのは久々でしたが、非常に内容の濃いイベントで面白かったです。主催されたマイクロソフト様と登壇者の皆様、ありがとうございます。

以下、自分の理解の範囲で書いたまとめを記しておきます。

Attention is all you need !!! を入門するまえに!

(Microsoft 得上竜一さん)

Transformer論文 Attention is All You Need を読むための前提知識を紹介したセッション。

arxiv.org

Attention is All You Need の解説記事は日本語でもあります。

deeplearning.hatenablog.com


  • Attentionは、注目したデータに従って出力する仕組み
    • 例: 画像処理
    • 背景が映った画像をそのまま処理すると背景情報を取り込んでしまう
    • Attentionを使うことで人だけに注目することができる
    • 人間は、人間が移ったとき写真について背景を無視することを自然とできるが、それと同じ
    • Convolution を二層に分岐して片方でsigmoidで出力して最後に分岐を合流させる。sigmoidが0に近い値はその後の処理に影響を与えなくなる
  • SENet
    • Convolutionによって複数の出力を得る。縦横のエッジ、色、明るさなど
    • 普通のCNNではこれら全ての特徴を後続の層でも利用する
    • 人間が注目する場合はどこに注目するかはケースバイケース
    • SENetでは画像の特徴をダイナミックに決定するアテンションになる
    • 画像全体に対してAvg. Poolingを取り、Conv(1,1)で特徴をとり最後にSigmoidを取る
    • このような仕組みを各Conv Unit で使えば、少し演算量増える程度で性能を上げることが確認できている
  • 言語処理におけるAttention
    • 典型的な文章分類問題
    • 特定の位置にある単語に注目するようにネットワークを構成することができる
    • 単語の特徴量を得られているとすると、それに対しアテンションを計算し、特徴ベクトルに掛け合わせる
  • 翻訳
    • Encoderで特徴抽出を行い、Decoderで別の言語に書き出していく
    • LSTMでは単語から次の単語への出力を行っていく
    • 契約書などでは単語の対応などの正確性が必要
    • Attentionを用いて、最初に翻訳すべき単語を決める
    • その単語をもとに次のAttentionを決めていく
    • 各単語の特徴とDecoderの初期ベクトルの内積を取る。これがAttentionの初期スコアになる。このスコアにSoftmaxをかけると、それがAttentionのスコアになる
    • 画像のときはSigmoidを使って全てのピクセルで注目するか否かを表現していたが、言語の場合はsoftmaxを使って、周りに比べて注目すべきかどうかを考える
    • どのような翻訳をするのかを出力するQuery vector と Key vector内積をとってsoftmaxにかける
  • Self Attention
    • Query /Key / Value をEncoder / Decoder ではなく同じタイミングで特徴抽出を行う
    • ある単語は文章中の他の単語に依って意味が変わる
    • あなたを「嫌い」では「ない」
    • Self Attentionは他の単語に特徴づける動作をもたせることができる
    • 嫌いというQueryと全ての単語のKeyとの内積を取り、softmaxを出し、Valueとかけたものを現在の単語ベクトルに足す
    • Self Attentionで他の単語を使って自分自身を特徴づけることができる
  • Attention is All You Need
    • Transformerの話
    • RNNの問題点: 一つ前の単語の計算が終わるまで次の単語の計算ができない
    • RNNのレイヤーをSelf AttentionのレイヤーにおきかえるのがTransformer
    • BERT / GPT-2 などもTransformerを使う
    • これで基礎知識はついたのでこの論文を読んでね

生成系NLPの研究動向

(Microsoft 伊藤 駿汰さん)

今回このセッションを聞くために参加しましたが、非常にいい内容でした。

  • 文生成とは
    • 文字: 言語を表記するために使われる記号の最小単位
    • 単語: 文字を組み合わせて作られる、意味を表し構文上の働きを持つ最小単位
    • 文: 単語を組み合わせて作られるまとまりある考え
    • 単語列: 単語を並べたもの。文も単語列の一種
    • 文生成: 単語の数、単語の種類、単語の順番を決定すること
    • 一定の制限を入れないと解けない。次の単語を予測を骨格とした文生成
    • 次単語予測: ある単語列が与えられたとき、次に来る単語を予測すること。例: スマホの予測入力
    • ある単語列が与えられたとき、次にどの単語になるか確率を計算し、最も確率が大きい単語を選ぶ
    • 文生成最初の単語Aを予測し、AからBを予測し、という流れを文が終わるまで繰り返す
  • 文生成モデルの歴史
    • 文生成モデルの歴史は言語モデルの歴史と絡んでいる
    • 言語モデル: ある文wが生じる確率を与える確率分布P(w)のこと
    • P(w)がわかると
      • 複数文で尤度比較ができる
      • 分布からいくらでも文を生成できそう
    • 言語モデル
      • 文を構成する単語の数は可変
      • 1種類の言語には数万程度の単語が存在
    • P(w)の計算は無理
      • 逐次的アプローチで近似する
      • 次に来る単語はそれより前の単語が何かで決まると仮定
    • N-gram言語モデル
      • ある単語より前の単語全部を見るのがつらいので見る数を決めて計算を軽くする
      • 前のN-1個の単語の並びに対し、次にきそうな単語の確率がわかればP(w)が計算できる
      • たくさんのデータから統計的に確率は得られる
      • P(wi|wi+1-N, Wi-1)があれば次単語予測ができて文生成ができる
    • 統計的手法を用いる文生成
      • 現実には5-gram程度が限界
      • Pitman-Yor過程を使って可変長N-Gramなども使われる
      • 現代ではあまり使われていない
    • DNNの学習
      • 入力と正解が必要
      • 予測が正解に近くなるよう学習を進めていく
      • RNN言語モデル(Mikolov 2010)
      • RNN (Rumelhart 1986)
      • P(w)が得られない変わりに、ある単語をいれたとき、次に来る単語の確率を予測させる
      • 入力: 単語、正解: 次の単語
    • RNNの問題点
      • プレーンRNNは遠い過去のデータの情報が急速に消えていくもしくは爆発的に増大していく
      • LSTM( Hochreiter 1997)GRU( Cho 2014) といった手法によって十分学習可能な水準に到達
    • Seq2Seq (Sutskever 2014)
      • 文の意味を取り出すエンコーダーRNN、取り出した意味から文を生成するデコーダーRNNをつなげて、文から文への変換を行うモデル
    • S2S + Attention (Luong 2015)
      • 過去の情報を重み付けして再度利用するAttention機構をSeq2Seqに追加して精度を改善したモデル
    • 生じてきた問題: RNNは過去の情報を処理してからでないと処理できない
    • Transformer (Vaswani 2017)
      • Seq2Seqと同じ文変換を行うモデル
      • 再帰構造を持たないNNとAttentionのみで構成され、高速
      • 翻訳タスクにおいて、RNN系手法よりはるかに少ない学習でSOTA
      • 長期依存を取れない問題を解決
      • 軽量・高速で並列化向きの構造
    • BERT (Devlin 2018)
      • 巨大な12層のTransformer Encoder
      • 2種類の言語モデル的事前学習
        • マスクされた単語の予測
        • 文の連続判定: 2つの文が連続した文であるかどうかの確率値
      • 膨大なデータで事前学習 + 少数のデータで目的タスクに転移学習
      • NLPの幅広いベンチマークでSOTA
      • 少量のデータで学習できるというのが産業利用上非常に大きかった
      • 工夫された事前学習によってTransformerの双方向学習が可能に。言語モデル的事前学習の有効性を示す
    • GPT-2 (Radford 2019)
    • UniLM(Dong 2019)
      • Transformer Prefix LM を使った事前学習モデル
      • 複数種類の言語モデル学習
      • 読解系タスクでBERT並、生成系タスクでSOTA
    • T5 (Raffel 2019)
      • Encoder - Decoder 構造を持つ巨大事前学習モデル(Transformerと同じ)
      • 全てのタスクを文変換として事前学習を行う
    • GPT-3 (Brown 2020)
      • 1750億パラメータの超巨大モデル
      • BERT: 3.4億
      • T5: 110億
      • 構造はGPT-2を踏襲
      • モデルのパラメータを増やすことで少ないデータの転移学習でも性能を出せるようになる
    • トレンドの推移
      • 2018年
        • LSTM→Transformer
        • Transformer改善
        • BERT
      • 2019年
        • BERTの流れを組む巨大事前学習モデル
        • 生成系タスクでGPT-2, T5が台頭
      • 2020年
        • NLPGPUとお金で殴る世界。GPU4000台並べるような世界
        • GPT-3
  • 今後のトレンド予測
    • Self-AttentionなどのTransformerの各要素の有効性についての知見が集積
    • Transformerは精度改善や軽量化の亜種が登場
    • GPT-3の方向性は実用上極めて重要
  • 発表者の研究
    • Memory Attention
    • Seq2Seq に発話に対する応答生成をAttentionを使って選んでいく
    • 研究から得られた知見
      • Attentionとは実質類似度の計算、情報の抽出に利用できる
      • 計算負荷も小さい、大小関係、解釈性
      • Transformerは新しい情報の流れを追加することが難しいが、LSTMベースのSeq2Seqは比較的簡単
      • シンプルなため、実装や構造検討に時間がかからない、とりあえずの実装として価値あり
      • 文生成の自動評価は難しい、対話系生成モデルは自動評価が極めて困難
        • BLEUやMETEOR等もあるけど不適切

NLPソリューション開発の最前線

(ISID 深谷勇次さん、小川雄太郎さん、ファイサルさん)

ISIDが5月にリリースした新製品のアーキテクチャ言語モデルの解説。

isid-industry.jp

  • フロントエンド
    • Azure Blob Storage に静的サイトをデプロイ
    • Vue.js / Nuxt.js フレームワークで静的サイトを構築し、axiosでバックエンドと通信
  • APサーバ
    • Azure VM上でDocker Compose を使ってコンテナ管理
      • ACIはマルチコンテナ運用が困難で、k8sは重たかった
    • Python + Django REST Framework
    • 非同期処理: Celery + RabbitMQ
    • DB: Azure PostgreSQL
    • 運用監視・ログ収集: Azure Log Analytics & Azure Logic Apps
  • MLサービス
    • Azure ML
    • GPU搭載のDSVMをAzure ML に紐付け
    • APサーバからMLサービスを経由しDSVMにPythonファイルと引数を投げて実行
  • 言語モデル
    • ISIDオリジナルALBERTを作った
    • 業務システムに組み込む要件としてGPUリソースのコストとパフォーマンスの問題があったため、高速・小規模なモデルを選定した
  • ALBERT
    • 埋め込み行列の因数分解
    • レイヤーパラメーターの共有
      • 12個のレイヤーで1つのパラメータを使う
    • NSPの代わりにSentence-Order Predictionの新補助タスク
    • LAMB アルゴリズム使用
      • 大きいバッチで学習
    • n-gram マスキング
    • SentencePiece対応
  • ALBERT日本語版はまだ1つしかない
  • ISIDオリジナルALBERT
    • モデル長1024語
    • Sudachiを使用。これはNICTの実験結果により、事前に形態素解析を行ったら良い精度が達成できることがわかっているため
    • Whole Word Masking (WWM) を使う
    • コーパス: Wikipedia日本語
    • トークナイザー: Sudachi モードC + Wordpiece
    • Livedoorニュースでファインチューニング
  • 今後の課題
    • 最長の長さを1024にしたため推論時間に影響がある
    • ナレッジ蒸留手法 DistillBERTなどの検討、Sparse Attentionの使用

Azure ML 自然言語処理の最新動向

(Microsoft 女部田啓太さん)

github.com

  • Classical Text Explainer
    • 古典的な機械学習パイプライン
    • sklearn の線形モデル coefs_
    • Treeベースのアンサンブルモデル feature_importances
    • デフォルト 1-gram BoW + sklern CountVectorizer + LR
  • Unified Information Explainer (Guan 2019)
    • MSのSOTAの研究
    • 相互情報量をベースにした post-hoc のアプローチ
    • DNNの隠れ層についての説明
    • 現在はBERTのみ対応
  • Introspective Rationale Explainer (Yu 2019)
    • MSのEMNLPで発表した研究
    • モデル学習の仕組みに埋め込むタイプ
    • 内省的生成機(Introspective Generator) を前処理で利用
    • 入力テキストを根拠(raitionales) と半根拠(anti-rationales) に分岐
    • 根拠のみを使って精度が最大になるように学習
    • モデルは入力テキストから生成された根拠しかみない

オープンコレクターに入社して3ヶ月が経った

f:id:shiumachi:20200701012420j:plain

Photo by Zoltan Tasi on Unsplash

shiumachi.hatenablog.com

オープンコレクターに入社してからあっという間に3ヶ月が経ちました。

ここでの仕事は、とにかく密度が濃いです。ミーティングは週2-3回ある程度で、余計な割り込みが一切なくひたすら技術に関係する仕事だけに取り組んでいます。

こんなにミーティングがないのはClouderaの最初の1-2年のときくらいかもしれません。


仕事は案件対応をやっています。HadoopNLPなど、自分がClouderaやLuminosoで得た技術をフルに使う仕事をやっています。

二年前、別の分野で一から始めたいと思ってClouderaを飛び出しましたが、結局仕事になるのは自分が培ってきた技術や知識なので、なかなかそこから逃れるのは難しいですね。

shiumachi.hatenablog.com

データ基盤と機械学習/NLPの知識を両方持っているというのはそれなりに希少価値があるはずなので、このあたりを自分の得意分野として伸ばしていければいいかなと考えています。


とはいえ、いざ案件対応すると、今まで習得した自分の知識だけでは全然足りないということを実感します。

ベンダーにいた頃に想定する環境は、いわば理想的なきれいな環境でした。実案件においては、お客様のビジネス要件や様々な制約を伴う環境と直面します。

あるべき論は通用せず、目の前にある様々な条件を考慮した上で課題を解決していく必要が出てきます。

正論の知識だけでは全く通用しない、実戦の技術というものが要求される中で、自分の実力不足を日々痛感しています。


仕事の負荷としては全く高くなく、非常に快適です。

10年ぶりに深夜・早朝のメール・チャット・ミーティングなどに悩まされない生活を過ごしていますが、これは精神衛生上とてもいいです。規則正しい生活になるだけで身体の負担が全く違います。

通勤も全くないので、仕事量の割には疲労が少なく、かなり健全な生活サイクルになっています。


英語を日常的に書かなくなったというのも大きな変化です。

海外とやりとりすることもあるので全くないというわけではないのですが、基本的には日本語だけで仕事ができています。

とはいえ、あまり自分にとってはどちらの言語使ってもそんなに変わらないので体感的にはあまり気になりません。

前回の記事でも書いたように、社長の @moriyoshit をはじめ、オープンコレクターのメンバーは技術力が非常に高く、一緒に仕事して楽しい人達ばかりです。

いい人達と一緒に仕事をするということの方が、言語的な問題よりも大きいとあらためて実感します。

shiumachi.hatenablog.com


自分の実力はまだまだですが、さらに知識と技術を身につけていき、新しい取り組みを色々とこなしていきたいと考えています。

在宅勤務で自宅トレーニングを長く続けるための7つのコツ

最近在宅勤務する人が増えてジムが利用禁止になったからか、 AIパーソナルトレーナーアプリのFreeleticsを始める仲間が増えてきました…が、どうも皆さんきついのか長続きしないようです。

6割近くの人がコロナ太りしたというニュースもあり、在宅勤務において継続的な運動は必須の習慣となっています。

www3.nhk.or.jp


そこで、Freeleticsを1年3ヶ月続けた経験から、自宅トレーニングを長く続けるコツをいくつか紹介したいと思います。

過去のFreeleticsの記事も参考にしてみてください。

shiumachi.hatenablog.com

shiumachi.hatenablog.com

shiumachi.hatenablog.com


f:id:shiumachi:20200609005030p:plain

大原則: 長続きさせることを目的にする!

レーニングが長く続かないのは、「一日でも早く痩せなきゃ」とか、「以前よりももっと高い負荷でやらなきゃ」とか、キツい目的を掲げるから逆に尻込みしてしまうというのも原因の一つです。

まずそういうのを一旦おいて、とにかく長続きさせるということだけ考えてください。重要なのは、「長続きさせれば楽でもいい」ということです。

ちょっとこの辺はFreeleticsの思想と外れますが、重要なのは習慣化であり、習慣化したあとにゆっくり負荷を上げればいいと私は思ってます。

今日スクワット100回やって2日休むよりも、3日間毎日50回やる方が結果的にたくさんの運動を行っています。

習慣化することだけに注力しましょう。

コツ1: 疲れてるときは休むことを恐れない

1つ目の話を踏まえた上で、疲れたらきっちり休むというのを心がけましょう。

オンライン飲み会のあとで寝不足だったり二日酔いだったりするときは無理してやるのをやめましょう。

その代わり、その日のトレーニングは「体調を整えて、早く寝る」ことだと気持ちを切り替えます。

体調を万全に整えた方がやる気が出ます。

コツ2: ブランク明けのトレーニングは軽めに

サボったりするとどうしても今までのトレーニングがきつく感じて、「今日はやめとこう…」と尻ごみしてそのままトレーニングをやめてしまいます。

今までの進捗が後退するのはどうしても抵抗を感じてしまいます。

勇気を出して、しばらくはとにかく軽めなものをやるだけにしましょう。

レーニングを再開したというのはそれだけですごいことです。喜びましょう!

幸い、最新のFreeleticsではその日の強度を変更する機能がついているので、勇気を出して負荷を軽くしましょう。

f:id:shiumachi:20200609003113p:plain

コツ3: ウォーミングアップとクールダウンはサボらない

他の人に話を聞くと、結構みんなカジュアルにウォーミングアップとクールダウンをサボっているので驚きます。

ウォーミングアップとクールダウンは絶対にやりましょう。これやるだけで疲労が全然違うし怪我のリスクも下がります。あとはトレーニングのやる気を出すための儀式的な意味もあります。

f:id:shiumachi:20200609003442p:plain

コツ4: トレーニングウェアを着る

前は普通のTシャツをそのまま使っていたのですが、やっぱりトレーニングウェアを用意した方が気分的な意味でトレーニングをやる気になります。

安いものでいいのであった方がいいでしょう。

コツ5: フィットネスマットを敷く

怪我防止や防音効果も重要ですが、トレーニングウェアと同様、やはり「これからトレーニングをやる」という気持ちを高めるためにもあるに越したことはないです。

自重トレーニングはかなり負荷がかかるので、可能な限り厚めのマットを選んでおくといいでしょう。


コツ6: トレーニング用スマートウォッチをつける(オプション)

それなりの値段がするので「できればあった方がいい」程度ですが、トレーニング中に心拍数を見て運動強度を見たり、心拍数記録から運動状況を把握するのに使うことはできます。

私は Fitbitしか持っていないのですが、Freeleticsのスマートウォッチ連携はApple Watchにしか対応していません。

お金に余裕があるとか、すでにApple Watchを持っているならそれを使うべきですが、「これから運動を始める人のやる気を高める」という目的で進めるにはあまりに高いんですよねApple Watch……。

個人的には、今何もスマートウォッチ持っていないなら、3-5万するApple Watchを買うよりは最安1万弱で変えるFitbitの方がいいと思います。私は上位機種のChargeを使ってますが、それでも1万円台です。

コツ7: 過度な食制限と並行でやらない

運動でつらい思いをしながら、糖質制限とか無茶な食制限をすると、凄まじいストレスがかかります。

ただでさえ自宅から出れなくてストレスが溜まっている状況でこんなことしたらそれだけで心身ともに病気になってしまいます。

食制限しなくても運動だけで6kg痩せたので、食事は気にせず食べた方がいいです。

shiumachi.hatenablog.com

余談ですが、今年に入ってからほぼ自宅から出ていませんしお酒も甘いものも普通に飲み食いしてますが、さらに3kgほど痩せました。

まとめ

レーニングを始めようというときはキツいトレーニングも頑張ろうという気持ちが高くてある程度は出来ますが、それだけでは長続きしません。

とにかく、常に「どうすれば習慣化できるか」「どうすればサボっても復帰できるか」を考えながらやることが重要だと思います。

健康的な在宅ライフを!

Freeleticsの購入方法

Freeleticsは有料のアプリです。年間1万円のサブスクリプションです。
3ヶ月プランや6ヶ月プランもあるので、続ける自信がないという人はこちらを購入してもいいです。14日間は返金に応じてくれるので、試しに買ってみて、合わなかったら返金しましょう。

Freeleticsを始めてみたいという人は、下記のリンクから購入すれば20%オフで買えます。 Coach と Nutrition (食生活改善)の二種類が出てきますが、Nutrition は自分は試していません。運動だけなら Coach で十分と思いますが、誰か Nutrition を試した人がいたら感想教えてください。

https://www.freeletics.com/r/124871187

とびきりのハッカーと同じチームで仕事をすることは福利厚生である

先日、社長の moriyoshi と一緒にある案件を行っていました。
リリース直前の前夜、どうしても現在のライブラリでは技術的に不可能な問題が発覚しました。
入社して一ヶ月も経てば、仕事の場でmoriyoshiがどう動くのか大体わかるようになります。

「じゃあ作るしかないな」
「できそう?」

moriyoshiは答えません。次の答えはもう分かっていたので、私はmoriyoshiが作成しているであろうプラグインを組み込むことを想定したテストコードとそのデプロイ手順を確認します。

私がテストコードとデプロイの準備を整えた頃、moriyoshiはSlackに再び現れました。

「できた」

予想通りの答えでした。私はすぐさまプラグインリポジトリに取り込み、テストを実行し、デプロイ作業を実施しました。

~~~

最近は、特に外資系ではどの会社も様々な福利厚生を用意しています。

Googleランチ育児休暇は有名ですし、マイクロソフト新型コロナウィルスに対応するため全従業員に12週間の有給休暇を付与するなど、世界に名だたる大企業は争うように手厚い福利厚生を提供することで人材の獲得に必死になっています。

しかし、どのような会社も、とびきりのハッカーと同じチームで仕事ができることを福利厚生として提供してはいません。

もちろん、上記の会社には優秀な社員がたくさんいるのは知っていますし、オープンコレクターだって別にそんなことを福利厚生として定義しているわけではありません。

ですが、金銭以外の報酬によって社員のロイヤリティを高めることが福利厚生であるならば、moriyoshiと一緒に仕事することは間違いなく自分が今まで経験した中で最高の福利厚生です。


オープンコレクターは、Joel Spolsky がソフトウェア開発者採用ガイドの中で言うところの、「頭が良く、物事を成し遂げる(p.95)」の集団です。オープンコレクターのメンバーは誰もが高い技術力とチーム同士の協力を惜しまない、本当に優秀なエンジニアの集まりです。

ソフトウェア開発者採用ガイド

ソフトウェア開発者採用ガイド

  • 作者:Joel Spolsky
  • 発売日: 2008/03/20
  • メディア: 単行本(ソフトカバー)

その中でも社長のmoriyoshiは正真正銘のハッカーであり、見栄えのいいパワポ資料を作ったり、世界を変える壮大な夢を描いて万人にそれを語ったりする代わりにコードを書くことで価値を示す、まさに「ハッカーと画家」から飛び出してきたような、とびきりのハッカーです。

一流のハッカーのそばにいて一緒に仕事をしながら学ぶことができるという福利厚生は、どんなにお金を積んでも買えるものではありません。


このような恵まれた環境で働くということに幸せを感じるとともに、いつか自分もそう思われるよう、努力を重ねていきたいと思っています。

TEAM OF TEAMS: 米軍による、最新ITを駆使した21世紀の組織変革戦略


この本は、イラクアフガニスタンで米軍の司令官を務めた将軍が記した、複雑で予測不可能な社会に対応するための、21世紀の組織論を書いた本です。

20世紀においては、事業部制に代表されるような、きれいに組織化され、作業を分割され、個人や部門が特定の作業に集中できるようにする効率化された組織が大成功を収めました。その集大成とも言えるものが、イラクアフガニスタンに派遣された米軍でしたが、装備や練度で大幅に劣るはずのAQI(=アルカイダ)に翻弄されていきます。その中で著者は、20世紀型のマネジメントに原因があると気づき、組織の構造の根本的な改革に取り組みます。そのときに着目したのが「チーム」という存在でした。

チーム単位の組織論は20世紀においても十分な進化を遂げていました。チームは互いを信頼し、全ての情報を透明性を持って共有し、硬直化した意思決定プロセスを捨てて誰もがアイデアを出していく、そのようなチームが成功を収めていました。その代表例は航空機業界におけるCRM(クルーリソースマネジメント)米国海軍のNavy SEALs などです。

しかし、当時の米軍では、チームレベルでは協調ができていても、組織全体は完全な縦割りで、組織の壁でコミュニケーションが遮られていました。「自分のチームさえよければ他のチームはどうでもいい」という感情は、チーム間の競争を生み、組織全体の目的よりも優先されてしまうこともあります。著者は、組織全体としてチームと同様の一体感を作るには、「チームの中のチーム(TEAM OF TEAMS)」が必要だと考えるようになりました。

ネイビーシールズのような、一体感を持ったチームを、部門横断でいくつも作ることで、全員が互いの顔を知らなくても、2-3ホップでチームとして繋がれるようなネットワークを組める、というのがアイデアの骨子です。では、それをどう実現するか?著者がそのときに採用した三つの施策が、情報統制の撤廃による透明性の向上、要員交換プログラムによる横の結束の構築、そして徹底的な権限委譲でした。

米軍では、オペレーションアンドインフォメーション状況報告、通称O&Iと呼ばれる会議が存在します。一般企業でいう進捗報告会議です。2003年の米国において著者が実施したことは、セキュアなビデオ会議の導入でした。

最先端の軍の装備と言われたら普通は兵器を想像するはずで、スカイプの拡大版だとは思わないだろう。(p.289)


Skype(最近ではZoomの方が一般的でしょうが)のようなビデオ会議ツールを、著者ははっきりと「兵器」と言っています。それだけではなく、「チャットルーム、ウェブポータル、そして電子メール(p.290)」、要するに今でいうところのグループウェアを導入していったのです。2020年の今となってはMSやG Suiteを導入するのは当たり前ですが、当時はまだ2003年です。その時代に、著者はグループウェアを組織変革のための「兵器」とみなしていたのです。

そして、著者はこの進捗会議を全員参加にするだけでなく、どんなに極秘な情報であっても全て公開情報としてこの会議で取り上げました。米軍のようなミッションクリティカルな領域においては情報を秘匿するのが常識でしたが、著者は共有する方がリスクよりもメリットがあると判断しました。

役に立つものは目に見えないが、漏えい事件は新聞のトップ記事になる。そのせいで、判断を誤ってはならない。…経験上確かなのは、情報を共有すれば膨大な数の命を救えるということだ。(p.300-301)


二つ目の施策は、要員交換プログラムでした。チーム間で人員を転属させる仕組みなのですが、部隊からは強い反発を受けました。長年の訓練で築き上げた強い結束を崩して他のチームと組むなど言語道断といった感じです。しかし、いざ命令が下りると、各隊は部隊代表としてエースを送り込み始めます。こうしたエースは他者との関係を築く才能があることが多く、新たなチームを作り出すことができます。それだけではなく、各チームからの代表がそれぞれのチームとのパイプ役となり、チーム同士の対立を避け、勝利のためのチーム同士の結束が可能となっていきました。

同時に、O&Iもチーム同士の結束に活用していきました。リソース(多くの企業でいうところのヒト・モノ・カネ)の部署間の取り合いはどの組織でもあることですが、このリソース配分会議をO&Iの最中に、すなわち部隊の全員が見ている中で行い始めました。これにより、部隊全員が全体像を把握することができるようになり、勝利を目指すためにリソースを譲るべきかどうかを判断できるようになりました。

最後の施策が、権限の委譲でした。部隊の司令官だった著者は、部下から作戦の説明を聞いて、それに対して承認する(日本で言うところのハンコを押す)だけの役割が多かったのですが、一瞬のチャンスを逃さないためにはそれは不要だと考えるようになりました。

現代の技術を使えば、戦場のあらゆる情報をリアルタイムに指揮官の元に集めることができ、また指揮官の号令は世界中どこにでも一瞬で届けることができます。しかし、それこそが、現場の自律的な活動を阻害する要因であると著者は気づきました。

(あらゆる情報がリアルタイムに集まる環境について)頭のなかに全体像を描くには素晴らしい環境だったが、そのせいで悪夢のような書類仕事と承認手続きが生じ、本当の問題を解決するために使えたはずの時間を奪われてしまった。…今では、最も優秀なリーダーですら、必要とされる判断のスピードと量に追いつけず、組織の下のものに権限を与えざるを得ない(p.364)


そして、著者は個別の作戦の判断をやめて、一連の流れを監督することに徹します。これにより、一ヶ月間の作戦行動を月あたり10-18回から、月あたり300回まで増やすことに成功したのです。

著者は、上記の体験を元に、これからのリーダー像についても論じています。従来型のリーダーは、英雄的リーダーでした。我々はリーダーに対し、高度で戦略的な先見性を求める一方で、些細な問題についても知っていることをリーダーに求め、知らないとなればなぜ知らないのかと追い打ちをかけたりします。しかし、そのようなリーダーはもはや成り立たないと著者は記しています。

リーダーは、自分が複雑な状況を理解し、予測できるような気になってしまう。しかし、変化が早く、相互依存的な環境においては、我々の目が届くスピードより問題が深刻化するスピードの方がずっと早い。(p.387)


ではリーダーは不要なのか?そうではありません。著者は新しいリーダーのあり方をこう記しています。

上に立つ者の役割は、糸を引いて人形を操ることではなくなり、共感によって文化を創造することになったのである。(p.388)

新たな環境でうまく機能するリーダーシップとは、チェスより菜園づくりに似ていると考え始めた。…組織を育て、構造や手続き、ひいてはその文化を改善し、配下の組織が「賢く自立的」に動けるようにする方が効果的なのである。(p.392)

より多くのことを決定する力を持ったまさにそのときに、自分の決断機会を減らさなければならないと気づいたのである。(p.394)


著者は、何を優先すべきかを話し合い、率先して手本を示すことでそれを明確化することで、部隊の関心をそこに集中させることが自分の第一の仕事だと認識しました。イントラネットに伝えたいことを、シンプルに、繰り返し記述し、行動とメッセージがぶれないように気をつけました。若いメンバーの意見は、たとえ知っているようなことが多かったとしても、聞いていたという姿勢を見せ、出来がよくなくても褒めることで、隊員に自信を与えていきました。

また、頭に浮かんだことを声に出して、思考プロセスを共有することで、部隊全体に考えを共有したりもしました。視察中は常に誰かとのコミュニケーションの時間に費やし、自分の考えを共有していきました。

テクノロジーは…従業員の働きぶりを細かく見張るためではなく、チームの一人ひとりにリーダーの姿をみせるために使わなければならない。リーダーは指示を出すよりも、自分の透明性を示さなければならない。これこそ新たなリーダーの理想像である。(p.405)


この本を読み終えたとき、私の脳裏に浮かんだのは、まだ200-300人の頃のClouderaでした。今でも私にとってはあの頃のClouderaが最高の組織だったと信じて疑っていませんが、それはおそらく、この本でいうところの「大きい1つのチーム」だったからだと感じました。創業者のMike Olsonは300人規模になるまで、全社員の最終面接を行っていたのですが、彼はそれが会社の文化を守るのに必要だからと確信していたからだと思っていたのは間違いありません。実際、当時のClouderaは、非常に透明性が高く、部門を超えて連携する、一つのチームとして機能していました。しかし、会社が大きくなってからは、One Clouderaという標語を明記するのに反して、部門ごとの縦割りが進んでいき、以前のような一体感を失っていきました。

唯一の例外はサポートチームで、サポートチームだけが初期のCloudera文化を引き継いだまま大組織化に成功しましたが、この本を読むと、おそらくサポートケースを対処するための部門横断チーム、すなわちTEAM OF TEAMSを作っていくことに成功したからではないかなと思いました。実際、初期の頃のClouderaでは、サポートケースに開発チームやポストセールス、営業などが書き込んでいき、お客様との対応を全員で行っていたのでそうしたTEAM OF TEAMSを作る土壌はあったように思います。

この本に書いてあることが本当に正しいか、まだ私は確信は持てていません。一つは、結局米軍は大統領、あるいは国から目標を与えられていく存在のため、自ら目標を作っていかなければいけない組織においては成り立たないのではないかという疑念があります。もう一つは、米軍は当然優秀で士気が高い精鋭集団のため、そこまで優秀ではなく士気も高くない組織でこの理屈が成り立つのかという懸念です。しかし、私が過去の経験から抱いた疑問にいくつかの解答案を示しているものであり、その意味でこの本は大きな価値があると感じました。

本書にもある通り、この本はノウハウ本のような「これをすればうまくいく」といった単純な内容ではありませんが、組織論としては面白い内容なので、そういうのに興味がある人は読んで損はないと思います。

ただ、前半250ページくらいは従来の事例の話が多く、組織論をある程度かじっている人は前半は読み飛ばして、O&Iの話あたりから読み始めればいいと思います。


(2020/04/27 追記: 細かいtypoの修正)