The Dabsong Conshirtoe

技術系の話を主にします。

経験学習入門を読む(3) 〜経験から学ぶための3つの力〜

この記事について

経験学習入門を数年前に読んだ時の読書メモを掘り出したので読み返したところ、改めて学び直せる点がありそうと感じたので、一挙公開する記事です。

 

「経験学習」入門

「経験学習」入門

 

 本文

  • 経験から学ぶ力の三要素
    • ストレッチ
      • 挑戦的で新規性のある課題に取り組む姿勢
    • リフレクション
      • 行為を振り返り、教訓を引き出す
      • 行為中に振り返ることも大事
    • エンジョイメント
      • 仕事にやりがいや意義を見つける
  • ストレッチのための方略
    • 挑戦するための土台を作る
      • 地味な下積みにより仕事の感覚を身につける
      • 普段から小さなストレッチを繰り返すことで、やがて来る大きなストレッチをこなすことができる
      • 何歳であれ、新しい分野に飛び込む際には土台作りが大事
    • 周囲の信頼を得てストレッチ経験を呼び込む
      • 目の前の仕事の質を高くすることで、より面白い仕事が降ってくる
    • できることをテコにして挑戦を広げる
      • いきなり大きなことをしない
  • リフレクションの方略
    • 行為の中で内省する
      • 仕事の意味や背景を考えること
      • 行為後のリフレクションの質にも影響を与える。自分の頭で考えながら集中して行った仕事を振り返ることで、初めて意味のある教訓を引き出せる。
    • 他者からフィードバックを求める
      • 同僚や部下に積極的なフィードバックを求めることで、適切なリフレクションとアンラーニングを可能にする。
    • 批判にオープンになり未来につなげる
      • 批判を素直に受け取りつつ、取捨選択する。
      • 批判を受けた時に、過去を後悔するのではなく将来の成長に生かすことが大事
  • エンジョイメントの方略
    • 集中し、面白さの兆候を見逃さない
      • つまらない仕事でも集中してこなしていると、必ず面白いと思える何かがでてくるので、それを見逃さない。
    • 仕事の背景を考え、意味を見出す
      • 上からの目標を受動的に受けるだけでなく、積極的に新たな目標を設定する。
      • 無理矢理「自分は面白い仕事をしている」と思い込もうとするのではなく、その仕事を正しく解釈すれば、必ず意義が見えてくる。意味ががないなら意味のあるものに変えていけば良い。
    • 達観して後から来る喜びを待つ
      • 仕事の意味は後からわかることもあるため、あまり目の前の仕事にこだわりすぎない。

経験学習入門を読む(2) 〜経験から学ぶ〜

この記事について

経験学習入門を数年前に読んだ時の読書メモを掘り出したので読み返したところ、改めて学び直せる点がありそうと感じたので、一挙公開する記事です。

「経験学習」入門

「経験学習」入門

本文

  • 70:20:10の法則
    • 70は直接経験から学ぶ
    • 20は間接経験から学ぶ
    • 10は本から学ぶ
  • アンケートによると、新しい仕事や海外勤務、扱いづらい上司・部下と仕事をした場合に成長を感じられることが多い
  • 経験には与えられる側面と自ら作り出す側面があり、後者が大切。
    • 地味な仕事でも工夫して面白い仕事に変えるなどすることで機会を生み出す。与えられるのを待っているのは効率が悪い
    • 小野次郎「次はなにをやらせてもらえるのかな、と上から言いつけられるのをただぼーっと待っていてはダメです。自分からやることを探して、自分で技を磨き、率先して勉強しなければ、絶対に仕事は上達しませんよ」
  • 経験学習のサイクル
    • 具体的な経験をする
    • 内省する
    • 教訓を引き出す
    • 新しい状況に適用する
  • 内省をし、教訓を引き出すステップが重要。経験のしっぱなしでは成長しない
  • 経験学習サイクルは、直接経験と間接経験両方に適用可能
    • つまり、本などで他者の経験を見るときにも内省と教訓の引き出しが重要ということ
  • サイクルを回す際には、対象となる経験が「よく考えられた実践」と呼ばれる考えを満たすと活性化される
    • 課題が適度に難しくて明確
    • 実行した結果についてフィードバックがある
    • 誤りを修正する機会がある

経験学習入門を読む(1) 〜成長とは〜

この記事について

経験学習入門を数年前に読んだ時の読書メモを掘り出したので読み返したところ、改めて学び直せる点がありそうと感じたので、一挙公開する記事です。

 「経験学習」入門

 

「経験学習」入門

 

 

本文

  • 成長には2種類ある
    • 能力的成長
      • テクニカルスキル
        • 業務知識、業務遂行能力
      • ヒューマンスキル
        • コミュニケーション能力、管理能力、調整力
      • コンセプチュアルスキル
        • 論理的思考能力、戦略的に物事を考える力
    • 精神的成長
      • 仕事に対し適切な「思い」を持つこと
      • 自分対する思いだけでなく、他人に対する思いが大きくなることを成長と言う。
        • 「成長したい」だけじゃなくて、「相手のためになりたい」とか。
  • 成長のハシゴ
    • 初心者
    • 見習い
    • とりあえずの一人前
    • === ===
    • 中堅
      • 組織の3割くらい
      • 中核的な人物
    • === ===
    • 熟達者
      • 組織の1割くらい
      • 社内外に認めらるエース
  • プレイヤーとしての成長と、マネージャーとしての成長がある
    • プレイヤーは、専門領域を縦横に伸ばし、マスターを目指す
    • マネージャーでも、現場リーダーと部長クラスでは求められる能力が異なるので、別の成長のハシゴを登ると認識すべき
  • アンラーニング(学びほぐし)
    • ベテランになっても成長し続けるには、時代遅れになった知識を捨て、改めて学びなおすことが必要
    • ドルマネージャー -> シニアマネージャーと役職が変わった時にも意識する。

管理職のためのエラスティックリーダーシップ

エラスティックリーダーシップという本を最近読んだのですが、ここ最近読んだマネジメント系の書籍の中では最もしっくりきました。

目標が自己組織化されたチームである点は自分の職場の目指すところですし、3つのモードとそれごとのリーダーシップ、という図式も単純明快でわかりやすく、実践で活かしやすいと思いました。 

 

エラスティックリーダーシップ ―自己組織化チームの育て方

エラスティックリーダーシップ ―自己組織化チームの育て方

 

 

 ただ、この本は基本的には現場のマネージャー向けのプラクティスとして書かれていると思いますが、私が担当しているVPoEのような、複数のチームや組織全体を見るいわゆる管理職的な人にとってはどのように活かせるのだろうかと考えをめぐらしながら読んでいました。

 

本書ではその辺りについて、第10章「管理職のためのマニフェスト」で軽く触れられており、モードごとのプロジェクト、チーム、個人に分けて説明されています。ここでは、それを参考にしつつ、評価育成や組織編成といった管理職の行う仕事の観点(と私個人の経験)からまとめてみました。

 

ここでの管理職の定義

  • チームリーダーの上長にあたり、開発チームに直接的な影響力がない
  • 複数のチームを管理する立場にあり、その規模は10名〜50名程度
  • ミッションとして、ラインの管理や、評価、育成、採用の制度設計といった組織づくりがある

 

活かせるであろうポイント

  • チームリーダーの支援、育成、評価の指針として
  • 採用、育成、評価制度設計の指針として
  • リーダー、メンバーの成長を支援する組織編成の指針として

 

チームリーダーの支援、育成、評価

管理職かそのチームやリーダーの状態確認のフレームワークとして、もしくはチームリーダーに、自身のチームの現在地の確認と将来的なプランを計画する手助けとして、エラスティックリーダーシップにおける3つのモードの概念が利用できるでしょう。

 

例えば、チームが日々のタスクをこなすのが精一杯で、中長期のプロダクトロードマップを見越した技術プランが練れていなかったり、なかなかメンバーのキャッチアップが進まずチームの力が上がってこないようなケースがある場合、

  1. チームがサバイバルモードにいるという共通認識をリーダーと合わせる
  2. 指揮統制型のリーダーシップを発揮して、チームを学習モードへ移行させるように指導する

という方向へ導くことが必要です。

 

学習モードに導くには時間の捻出が必要ですが、プロダクトスケジュールの見直しやチーム構成の変更などリーダーの範疇を超える対応が必要な場合は、管理職が手を打つ必要があります。(本で援護射撃と表現されているのはこのようなことかなと思いました)。

また、チームが良くない方向に進んでおり、品質やスケジュール面でのリスクがある場合、本書10.2.1「サバイバルモードのプロジェクト」に記載の通り、リーダーやチームにそのリスクを説明して対策すべきです。

リーダー自身がサバイバルモードにいる自覚がない場合には気づかせる責任も管理職にはあります。

同じように、学習モードから自己組織化モードへチームを持っていけるようなコーチングなりフィードバックなりが、管理職の仕事となってきます。

 

採用、育成、評価制度設計の指針

上記の直接的なコーチングと並行して、メンバーを含めた育成や評価の制度面にエラスティックリーダーシップのエッセンスを含めることで、組織全体の改善につなげられるでしょう。各チームへの間接的な支援です。

メンバーの評価指針はさまざまな観点があると思いますが、チームビルディングにどれだけ良い影響を与えられているか、という点が一つあると思います(チームビルディングはリーダーだけの責任ではないと考えています)。

チームを自己組織化モードに持っていけるような取り組みを率先して行えているメンバーや、サバイバルモードを抜け出そうとする行動をするメンバーを高く評価することで、組織全体を自己組織化チームを目標として動いてもらえるようにします。

バス因子を除こうとする行動や、プロジェクトの進行管理や仕様調整などをリーダーに頼らずともできる or できるようになる、という行動もそうです。

 

リーダー、メンバーの成長を支援する組織編成

管理職としては、ある特定のチームだけでなく、すべてのチームが自己組織化モードへと移行できるようにもっていくのが望ましいはずです。

ただ、特定のチームは現状のメンバー構成ではその状態に持ってくのが困難な場合もあります。

管理職が組織人事の権限を持つならば、チーム編成を変更することでその問題に対処します。

例えば、人材流出やプロジェクト状況の大きな変化によりサバイバルモードへと移行してしまったチームには、経験豊富なリーダーを異動や採用により投入し、指揮統制型のリーダーシップでサバイバルモードを脱出してもらう。

また、それとは逆に、チームが自己組織化モードに移行し、リーダーの仕事量が減っている場合、思い切ってリーダーを剥がし、別プロジェクトに挑戦してもらう。うまくいっているチームの体制変更は迷いますが、リーダーが安全地帯から出られるようにする事でリーダーにとってもチームにとっても今以上の成長環境を提供できるはずです。

 

自己組織化チームにおけるチーム体制変更の体験談

私個人の経験ですが、以前に自己組織化されたチームのリーダー務めていたことがあります。もちろん最初からそうではなかったのですが、2年くらいチームの運営をしたころにはメンバーは自立し、私の役割は大枠でのファシリテートとレビュー、技術的な改善を率先して行う、くらいでした。次期のリーダー候補もしっかり成長しており、正直自分がいなくても回るだろうなという感覚が強くありました。

 

そんななか、次期リーダー候補メンバーに更なる刺激を与えたいという思いと、自分自身も新たなプロジェクトに挑戦したいという思いからチームリーダーをメンバーに譲り、私は新規プロジェクトのリーダーとなりました。

 結果、もとのチームはチーム編成やビジネス環境の変遷はありながらも新リーダーのもと安定してプロダクト開発を続けられており、私自身も新製品の立ち上げや組織作りなどに挑戦できるようになり、お互い、ひいては組織全体にとって良い人事になったなと思います。

 

まとめ

管理職におけるエラスティックリーダーシップとは、リーダーに対するコーチングを基礎とした個々のリーダー/チームのモードに応じた支援と、育成評価の制度設計や採用計画などのシステム作りにより、組織全体のモードを管理することにあるのかなと考えました。

PyConJP 2016で「Pythonで入門するApache Spark」を話した

1ヶ月近く過ぎてしまいましたが、今年のPyConJPで、「Pythonで入門するApache Spark」というタイトルでスピーカーを務めさせていただきました。

資料

Jupyterコード

動画

PyConJPには参加者として2012年くらいから参加していましたが、スピーカーとして参加するのははじめてでした。大きなカンファレンスで一度話してみたいという願望があったので、叶ってよかったです。立ち見の方もいらっしゃるなど、思った以上に人が集まりとても緊張しましたが、良い経験をさせていただきました。

発表資料の準備をすることで、Sparkの基礎をもう一度振り返ることができたのが良かったかな。時には人前にさらされることも大事ですね。

運営の皆様、発表準備を手伝っていただいた皆様、どうもありがとうございました。

会社ブログでも紹介していただきました。ありがとうございます!

SparkのShuffleについて調べてみる (4:Shuffle Readの実装探検)

前回の記事では、SparkのShuffle Writeの実装を追ってみました。

今回は、Shuffle Readの実装について調べていきたいと思います。(前回と同じく今回も個人的な理解の促進のためにこの日記を書いています。)

Shuffle Read

Shuffle Readは、Shuffle Writeにより書き出されたデータを読み出す処理です。処理するタスクの前段のステージがShuffleステージである場合に行われます。

Shuffle Readは、Sort ShuffleでもHash Shuffleでも同じクラス(BlockStoreShuffleReader)が使用されます。

データのフェッチ

BlockStoreShuffleReaderは、ShuffleBlockFetcherIteratorを使用して対象のデータ(ブロックIDとデータストリーム)を逐次取得します。この時、リモートにあるデータを一度にフェッチするデータの最大はspark.reducer.maxSizeInFlightにより制御されます。この値が大きいほどフェッチの効率はよくなりますが、メモリへのプレッシャーが増します。実際には、最大で5ノードへ同時にリクエストを送るためにこの値を5で割った数を上限としているようですが、どこでその「最大5ノード」という制限をしているかは不明でした。

どのBlockManagerからどのBlockIDを抜けばいいかは、MapOutputTrackerというmapタスクの出力を管理するオブジェクトが教えてくれます。

また、Shuffle Writeの方式によってデータの書き出され方は異なりますが、ここの違いはBlockManagerの中でShuffleBlockResolverにより吸収されています。例えば、Sort Shuffleの場合は前回紹介したIndexShuffleBlockResolverが使用されます。

フェッチされた各データは解凍とdesrializeがされ、キーに応じた集約が行われます。Map Side Combineが行なわれている場合はマージされた結果同士のマージ、そうでない場合は個々の値のマージが行われます。

データのマージ

マージにはExternalAppendOnlyMapというオブジェクトが使用されます。このオブジェクトは値のマージ方法を知っており、データを外部から受け入れてマージされた結果を返します。データが一定の水準に達するとディスクに退避するところが特徴的です。Shuffle Writeで登場したExternalSorterもデータがメモリに収まらない場合はディスクに退避する機能を持っていますが、どちらも同じSpillableというtraitで実現しています。

また、ExternalSorterでもデータの保持にはPartitionedAppendOnlyMapとうオブジェクトを使用しており、先のExternalAppendOnlyMapと同じ親(AppendOnlyMap)を持ちます。AppendOnlyMapではKey-ValueのデータがシンプルなArrayとして記録されるオープンアドレスのハッシュテーブルです。基本的にはキーが入っているインデックスの一つ後ろに値が入っているようです。

キーのポジションは、キーのハッシュ値とQuadratic Probingというキーの衝突回避の手法により決定されているようです。

こうしてマージされたデータがBlockStoreShuffleReaderから返却されます。sortByKeyのようなソート順序の指定がある場合、ExternalSorterによりソートが行われた上で返却されます。

以上です。ExternalAppendOnlyMapでspillしたデータとオンメモリのデータをマージするところなど、まだまだ深く潜り込めるポイントはたくさんありそうですが、ひとまずこの辺で。

SparkのShuffleについて調べてみる (3:Shuffle Writeの実装探検)

前回の記事では、SparkのShuffleについて、Physical Planから見た内容についてまとめました。

今回は、実行時の観点からのShuffle Writeについて調べていきたいと思います。(前回と同じく今回も個人的な理解の促進のためにこの日記を書いています。)

実行時のShuffleの流れ

Shuffleはどのように実現されているのかを簡単に見ると、以下の流れとなります。

  1. 各TaskがShuffleのキーごとにデータをファイルに書き出す(Shuffle Write)
  2. reducerごとに担当するキーのデータファイルを読み込み、処理を行う(Shuffle Read)

Shuffle Write

もう少し詳細に流れを追うと、以下の流れとなります。

  1. ShuffleMapTaskが起動されます。
  2. RDDのデータを読み込む。この時、前段のステージがShuffleの場合、ShuffleReaderからデータを取得します(Shuffle Read)。そうでない場合、入力ソースからデータを読み込みます。
  3. 得たデータをShuffleWriterを使って出力します。

ShuffleMapTaskの起動について

まず、TaskにはShuffleMapTaskとResultTaskの2種類の実装があります。ResultTaskは、foreachやcollectのような、ジョブの最終ステージを実行するタスクです。一方、ShuffleMapTaskはそれ以外のステージで実行されるタスクです。

DAGSchedulerでTaskを生成する際、対象のステージがShuffleMapStageである場合に生成されているようです。

ShuffleWriterを使った出力について

ShuffleReaderを使った読み込みについては後半で説明するので一旦スキップします。

ShuffleWriterによる出力について

どのようにデータをShuffleするかについて、Sparkではhash, sort, tungsten-sortの3つの実装がされています。どの実装を選択するかは、spark.shuffle.managerにより制御できます。

Hash Shuffle

Spark 1.2.0以前のデフォルト実装です。各mapタスクが各reducer(Shuffleされたデータを受け取る側)ごとにファイルを作成します。ただ、この方式ではMap Taskの数とReducerの数を掛け合わせた量だけファイルが生成されてしまうため、各ファイルを作ったり消したりするところでのオーバーヘッドが大きく、特にパーティション数が大量にある場合に問題なるケースが多かった様です。

spark.shuffle.consolidateFilesという、この問題に対処するオプションも1.6.0以前のバージョンでは存在しましたが、後述のsort shuffleの存在等の理由により削除されています。

Sort Shuffle

spark.shuffle.managerのデフォルト値です。上記のHash Shuffleの問題に対処するために導入されました。各Mapタスクごとにパーティション、キーでソートされたデータを書き出します。同時に、各ファイルごとにどのパーティションがどこから始まるかわかるようにインデックスファイルを別で書き出します。各Reducerは、このインデックスファイルを頼りに必要なパーティションのデータを読む形になります。

具体的に実装を見ていくと、この一連の処理を担うのが、SortShuffleWriterです。SortShuffleWriterは、ExternalSorterを使ってデータの書き込みを行います。

ExternalSorterでは、MapSideCombineが有効な場合、データはPartitionedAppendOnlyMapを使ってキーごとにマージされた形式でオンメモリに保持されます。内部で持つデータサイズが一定量に達した場合、ExternalSorterはspillという処理により、現在のデータをファイルに書き出し、新しいPartitionedAppendOnlyMapを生成します。この時の書き込みバッファはspark.shuffle.file.bufferspark.shuffle.spill.batchSizeにより制御されているようです。

spillにより作成されたファイルは、すべてマージソートされて一つのファイルとして改めて書き出されます。spillされたファイルは書き込み処理完了後に削除されます。

ファイルをすべて書き出したら、IndexShuffleBlockResolverを使ってインデックスファイルを書き出します。

spillの存在によりオンメモリに乗り切らないデータも安全にシャッフルできるのですが、余計なIOやマージソートが発生するため、処理速度になかなかのインパクトを及ぼします。(個人的にも、spillを削減することで処理速度が数倍早くなったケースがありました。)また、ディスクに小さいファイルを書きまくるのでディスクも圧迫します。spillの情報はSparkのUIから閲覧可能ですので、チューニングの際には参考にすると良いでしょう。

ByPass Merge Sort Shuffle

SortShuffleWriterが使われる設定で以下の条件を満たした時、BypassMergeSortShuffleWriterという特殊な実装が使用されます。

旧来のHash Shuffleと同じく、パーティションの数だけファイルを作成し、最後にそれらのファイルのマージと、パーティションの位置を示すためのインデックスファイルを作るだけというシンプルなものです。

SortShuffleWriteと異なり、データをインメモリに保持する必要がなく、spillも発生しないため効率的です。反面、Hash Shuffleと同じく大量のファイルが作成される恐れがあるため、少ないパーティション数の場合のみ有効といえます。

Tungsten Sort Shuffle

Project Tungstenの一環で、オフヒープを使ったより効率的な実装です。spark.shuffle.managertungsten-sortを指定することで有効になる、という記述もいくつか見ますが、実際にコードを見てみるとsortでもtungsten-sortでも同じ実装(SortShuffleManager)が使われていました。(ここは単に私の読み間違いかもしれません)

実装を見る限り、以下の条件を満たす事でUnsafeShuffleWriterが使用されるようです。

  • シリアライズされたオブジェクトを整列した結果が、シリアライズする前に整列した結果と一致すること(ちゃんと理解できでるか微妙です。ドキュメントを見る限り、シリアライズしたオブジェクトのバイトストリームに対して、ソートなどして順序を変更することが可能か、ということでしょうか)
  • Shuffleの処理に集約を含まない
  • パーティションの数が16,777,216より少ない

内部ではShuffleExternalSorterが使われています。Sort Shuffleで出てきたExternalSorterと同じく、内部のレコードはパーティションIDでソートされ、単一のファイルに出力が行われます。

この実装ではserializeされたオブジェクトを直接扱うため、Sort Shuffleで必要とされたserialize/desrializeの処理が不要となります。spillのマージには、FileStreamを使うバージョンとNIOのFileChannelを使うバージョンがあるようです。

また、データはオフヒープに格納され、そのアドレスとパーティションIDがShuffleMemorySorterという専用のSorterに格納されます。アドレスとパーティションIDは単一のLong型に変換されるため、1レコードで使うサイズは8バイト(!)です。

データを書き出す際は、TimSortにより内部の先ほどのデータがソートされます。