For Your ISHIO Blog

データ分析や機械学習やスクラムや組織とか、色々つぶやくブログです。

OST(Open Space Technology)をやってみた。

とあるイベントで、OST(Open Space Technology)という、参加者ドリブンで議論をするワークショップ手法を初めて経験しました。そもそもOSTとは何か、どうやって運営していたかなどをお伝えします。結論からいうと、結構カオスでした、でもそれが面白かったです。

OSTとは

OSTとは、Open Space Technologyの略で、1985年にハリソン・オーウェンという方が提唱した話合いを行うためのフレームワークです。

『Open Space Technology: UsersGuide』

https://elementaleducation.com/wp-content/uploads/temp/OpenSpaceTechnology--UsersGuide.pdf

世界各国の企業、行政、教育、NPOなどで高い成果を上げているらしいです。日本の教育系NGOで働いていた知人に「OSTってやったことある?」って聞いたが、そもそも知らなかったので、日本ではまだ馴染みがないかもしれません。

OSTは、一言でいうと、参加者自身の目的設定や問題解決を行う場です。

参加者自身が持ち寄ったテーマをもとに、複数のディスカッショングループに分かれて議論を行っていきます。複数の話し合いの場に分かれることから、割かし大人数の場でOSTが利用されることが多いようです。

主催者からは、以下を提示されるだけです。

  • テーマを持ち寄るためのテーマ
  • 場所・時間

「はい、じゃあグループに分かれてあとよろしく!」って感じなので、その中でどう進めていくかは集まった参加者の中で決めていきます。参加者の自律性・自主性が求められるワークショップ手法です。

私が知っている限りでは、スクラムマスターが集う定期イベント「Scrum Masters Night!」で、このOSTを活用したイベント設計がされています。

smn.connpass.com

OSTの原則と法則

ハリソン・オーウェン氏のUser's Guide の第5章「The Four Principles and The Law of Two Feet」では、OSTを行う上での原則が提示されています。

  1. Whoever comes is the right people.(ここに来た人は誰でも適任者である)

  2. Whatever happens is the only thing that could have. (何が起ころうと、それが起こるべき唯一のことである)

  3. Whenever it starts is the right time. (始まる時はいつでも、適切な時である)

  4. When it is over it is over. (いつ終わろうと、終わったときが終わりである)

これらはつまり、「来るもの拒まず、去る者追わず精神」なのかなと解釈しています。重要なのはこれを最初に共通認識として持つことで、不必要に他者の振る舞いに対して気を取られることなく話合いに集中することができます。

同章では、「The Low of Two feet(二本足の法則)」という約束事も提示されています。

話合いに参加すると決めた場合、参加者はそこで、「学習する」もしくは「貢献する」どちらかの状況でいる必要があります。

もし、どちらも満たすことができない状況にいると自身が感じたときには、学習か貢献ができそうな他の話し合いの場に移動した方が、自身にも周囲の人間にも有益だというものです。

OSTの流れ

OSTの進行は以下のような流れで進めます。

  1. オープニングセッション

    • 全員で円になり、主催者からOSTの原則と法則について、場所や時間について説明がなされ、参加者全員で共通認識をもちます。
  2. テーマ出し

    • 参加者が持ち寄った話合いのテーマについて、持ち寄った人間が発表を行います。今回は、その場でテーマ案を参加者から募ったので、どのように運営されていたかは後述します。
  3. 各テーマに分かれて、話し合いが開始されます。

  4. クロージング

    • 最後に各テーマでの話し合いについて、全体で簡単な共有と、主催者からクロージングがあり終了。

今回のテーマ出し方法

今回は、OSTをやることは事前に知らされていなかったため、その場で参加者から話し合いのテーマを募り、投票が多かった上位のテーマについて、別々の場所に分かれて話し合いが行われました。

テーマ出しには、sli.doというサービスが利用されました。

f:id:ishitonton:20190907164651p:plain
sli.do

www.sli.do

sli.doは、勉強会やカンファレンスにて、会場からの質問を匿名で集められるサービスです。指定されたハッシュタグを打つとチャット画面が表示され、スマホやPCから質問やメッセージを運営側に投げることができます。

このサービスを利用して、セッション参加者から匿名でテーマ案が投稿されます。またそのテーマ案に対して、各参加者から「いいね」ボタンが押されることで、興味関心が高いテーマを選出しました。

f:id:ishitonton:20190907165941p:plain

実際の話合いの場

投票が多かった5つのテーマについて、ホワイトボードが与えられ、話し合いの場が持たれました。実際にどんな感じだったかを書いていきたいと思います。

主催者からアナウンスが流れ、ここから同じテーマに集まったのは20人ほどで、進め方も含めて話し合いを行っていきます。ドキドキ。

自分含め、多くの人は知らない間柄の人間と、突然ディスカッションをすることに慣れていないですよね。誰も、ファシリテーションに名乗りをあげずに、30秒ほど空白の無言の時間をむかえました。

今回は選ばれたのが自分が投稿したテーマだったこともあり、まず自分がファシリテーションに名乗りをあげました。

周囲を見渡すと、半数の人が座っている状況でした。「二本足の法則」のもと、「参加する人はホワイトボードの前に集まろう」と声掛けをしました。すると、ぞろぞろとみんな集まってきてくれ、スタンディングでのディスカッションがスタート。

そもそも話し合うといっても、フレームワークを作らないと適切な議論はできないと感じ、どういったフレームワークで進めますか、と場への投げかけを行いました。

「各人が(テーマに関する)課題について、ホワイトボードに書いていくのはどうだろう?」「書記が必要ですかね?」「付箋があると便利なんだが」みたいなコミュニケーションがなされ、各人がホワイトボードに感じている課題を書いていく、みたいなスタイルになりました。

そのあと、上から各課題に対して、ホワイトボードに書いた人がしゃべっていき、それについて議論がなされた。という感じです。

OSTの感想

OSTは、参加者の当事者意識が求められます。しかしそのテーマに集まった人たちは、何かしらテーマに対する探究や情熱がある人たちなので、それなりに参加者からの発言が出てきやすいと感じました。

実際に、発言をしていたのは5~6人くらいといった感じでした。でもOSTの法則になぞらえれば、貢献していなくても学習してくれていればOKなので、ファシリテートをする中で全員の意見を促す必要がないのは楽でした。

難しさは、その進め方にあると感じます。同じチームの人間であれば「こういったフレームワークで進めよう」みたいなことができますが、初めてあった人たちと、共通の考えをもとに進行を行うのが結構難しいです。

でもOSTの記事を読んでいると「カオスなほどクリエイティブな発想が出て良い!」と書いてあり、結構OSTで格闘技のパフォーマンスをやったり歌うたったりみたいな話が出てくる。Open spaceでよりOpen Mindにならないと、OSTの威力は発揮できないのか?でもとにかく楽しいアドレナリンが出る経験でした。

社内勉強会でword2vecについて話しました

月に1回、社内でデータ分析の勉強会をやっています。今月はword2vecについて話をしました。

最近、ブログを書けていないので、資料だけリンクを張っておきます。

speakerdeck.com

なお、この資料は下記書籍に大きく影響を受けてます。

www.oreilly.co.jp

手抜きで恐縮ですが、以上です。

チームマネージャーに読んでほしい一冊。『対話型ファシリテーションの手ほどき』を読んだ。

チームマネージャーのロールとして、チームメンバーと1on1ミーティングをやったり、振り返りのファシリテーションをしているわけですが、より気づき・学びを増やしてチームが成長していくにはどうしたらよいかを日々自問しています。うまくできないことも多いです。

そんな中、親しき知人に紹介してもらった書籍『対話型ファシリテーションの手ほどき』がとても勉強になりました。

https://www.amazon.co.jp/対話型ファシリテーションの手ほどき-中田豊一/dp/499081472X

ファシリテーションの書籍はたくさんありますが、著者の成功体験だけに基づく主観性の強いビジネス書であったり、具体的な問いかけ方法がなくイマイチ実践まで結びつかなかったりすることが多かったです(個人調べ)。本書籍は、自分にはとても納得性の高い内容であり、体系的に具体的な対話方法まで記載されており、すぐに実践してみたいと考えています。

対話型ファシリテーションとは

対話型ファシリテーションは、正式な方法論の名称はメタファシリテーションといいます。ファシリテートする側が、当事者に対して事実のみを質問していきます。 このやりとりを通じて、当事者が思い込みに囚われることなく自分の状態を正確に捉え(気づき)、当事者自らが問題解決に必要な行動変化を起こすように働きかける方法です。

以下でも述べますが、できるだけ5W1Hの「Why」と「How」を質問せずに、「When」「Where」「What」「Who」などの事実を聞く質問を繰り返すことで、事実を積み上げ当事者を思い込みから解放し、気づきを導きます。具体的なテクニックをいくつか書いていきます。

Whyと聞かない

自分のこれまでの会話を想像してください。何か相手が語り始めたら「なぜ?どうして?」と原因や動機を尋ねることが多いと思います。

対話型ファシリテーションの手法では、英語の5Wのうち「なぜ?どうして?(Why)」を聞かないようにします。「なぜ?どうして?」と聞きたくなったら、それを一度飲み込んで「いつ?どこで?何を?」という質問に置き換えて質問します。試しに「一番最近いつ起きました?」「その前は?」と質問をしてみてください。

私たちは、特にあまりよくないことに関して「なぜ?」と聞かれたら、つい言い訳をするようにできています。本当かもしれないし、言いつくろいかもしれませんが、重要なのは語っている本人もその真偽が定かではないということです。

なぜ?と聞くことで相手の自分勝手な安易は原因分析をその場で始まることもあります。人間は、自部の都合の良いように解釈したがる傾向にあり、良い結果については自分の努力や能力、悪い結果については外的要因のせいにしたがります。課題分析を手助けする上では、こちらからWhyを尋ねないのが鉄則です。

Howの質問はナンセンス

先日、北海道出張から帰ってきた同僚に「北海道どうでした?」と質問をしました。いわゆるHowも使い方を誤るとコミュニケーションの阻害要因になります。

自分:「北海道どうでした?」

同僚:「よかったです」

両者:「...」

Howの質問は、尋ねる方は気軽かつ安易に質問できますが、答える方には手間・コストがかかる面倒な質問です。あんまり興味ないけど挨拶代わりに聞いとくか感じが出てしまっていますね。相手を戸惑わせたり、確信のない答えを強要したりする可能性が高い、良くない質問です。 ここでも、Whyを除いた4Wで質問することが重要です。

一般化された質問はNG

A:「週末は何をして過ごしたいですか?」

B:「週末はいつも何をして過ごしていますか?」

C:「週末は何をして過ごしましたか?」

A・B・Cの質問は、順に感情意見・考え事実を質問しています。Bは一見事実を質問してそうですが、相手の希望や思い込みも含めた意見が得られるでしょう。このような「いつも」「最近」などの一般化された質問は、相手の現実を浮かび上がらせることを阻害します。

当事者自らが気づくことが行動変化のエネルギー

問題解決の行動変化は、当事者自らの「気づき」が行動変化のための大きなエネルギーになります。それをわかりやすく伝えるための表現が次の4つです。

  1. 聞いたことは、忘れる
  2. 見たことは、覚えている
  3. やったことは、わかる、身につく
  4. 発見したことは、使う。

自分で見つけたこと以外はほとんど忘れるのが人間であり、忘れてしまうと行動変化に結びつけることができません。つまり、相手自身が答えを見つけるまで、粘り強く働きかけることが重要です。また、自分で何かを見つけたときは、気づきの喜びにも満たされます。

焦らず我慢強く、待つ

当事者自らが気づくことが重要ですが、気づくのには時間がつきものです。ファシリテータ自身は焦らず我慢強くどっしり構えて待つことが重要です。 ついつい、ファシリテータ側が待てずに答えを言ってしまうことってありませんか?質問に対して相手から具体的な反応がない場合でも焦らず待ちましょう。例えば、「自分のことで煮詰まったら、身近な人の成功事例を訪ねてみる」など、質問を変えてみるのも方法論の1つです。

質問の難易度を意識する

自分自身もこの本を読んでいて「質問攻めにして、相手を不快にさせないか?」が少し心配になりました。刑事ドラマの尋問のようになり、相手を委縮させないかと心配になります。そうしないための解決方法も記載されています。

1つ目は、相手のことに関心を持つことです。そのようなマインドセットを持っていれば、こちらが考えるほど嫌がらないとのこと(おそらく経験則として)。2つ目は、相手が答えられる質問をすることです。一生懸命考えを巡らせなくては答えられないような質問や、嫌なことを思い出させる質問は避け、心理的に答えやすい質問をすることで、相手にも心を開いてもらいます。

本当に解決すべき問題かを探る質問

相手から「〇〇の件で困っている」「●●が問題だと思う」などの発言が出てきたら、それが本当に解決すべき問題かを探るために、以下の質問をするとよいでしょう。

  • 一番最近、誰がどのように困ったか
  • それを解決するために、どんな努力をしてきたか

まだ、誰も行動に移していない時点では、問題ではなく願望の可能性もあり、それを見極める必要があります。ファシリテーター自体が「本当にそれは問題ですか?」と直接尋ねることはやめましょう。そう問うことは、ファシリテータ自身の考えや意見を直接伝えることになります。

まとめ

1on1や振り返り作業を効果的に実施することで、チームのパフォーマンスを高めることに自分は貢献したいです。この書籍は、そんな自分の現状に非常に手助けになる1冊になりそうです。

書籍を読んで感じたのは、練習することが、ファシリテーションの上達の近道そんな環境って誰もが得られるわけではないということです。自分は幸運なことにロールがあり1on1で週に数名と対話をする場があるので、この場を有効活用して自身も成長していきたいと思います。

また、これまで「なぜ?」と聞くことが多かったと思いますが、頑張って事実質問に置き換えてみます。おそらくこれまでと違ったパターンのコミュニケーションが生まれそうで、それも楽しみです。

言語処理100本ノック - 第2章: UNIXコマンドの基礎

思い立ったので言語処理100本ノックをやっていきます。

www.cl.ecei.tohoku.ac.jp

「第2章: UNIXコマンドの基礎」をやりました。

利用ファイル

hightemp.txtは、日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである。以下の処理を行うプログラムを作成し、hightemp.txtを入力ファイルとして実行せよ。さらに、同様の処理をUNIXコマンドでも実行し。プログラムの実行結果を確認せよ。

ファイルの中身は以下の通りです。

$ cat hightemp.txt 
高知県  江川崎  41      2013-08-12
埼玉県  熊谷    40.9    2007-08-16
岐阜県  多治見  40.9    2007-08-16
山形県  山形    40.8    1933-07-25
山梨県  甲府    40.7    2013-08-10
和歌山県        かつらぎ        40.6    1994-08-08
静岡県  天竜    40.6    1994-08-04
山梨県  勝沼    40.5    2013-08-10
埼玉県  越谷    40.4    2007-08-16
群馬県  館林    40.3    2007-08-16
群馬県  上里見  40.3    1998-07-04
愛知県  愛西    40.3    1994-08-05
千葉県  牛久    40.2    2004-07-20
静岡県  佐久間  40.2    2001-07-24
愛媛県  宇和島  40.2    1927-07-22
山形県  酒田    40.1    1978-08-03
岐阜県  美濃    40      2007-08-16
群馬県  前橋    40      2001-07-24
千葉県  茂原    39.9    2013-08-11
埼玉県  鳩山    39.9    1997-07-05
大阪府  豊中    39.9    1994-08-08
山梨県  大月    39.9    1990-07-19
山形県  鶴岡    39.9    1978-08-03
愛知県  名古屋  39.9    1942-08-02

環境

10. 行数のカウント

行数をカウントせよ.確認にはwcコマンドを用いよ.

# コマンドラインで実行してください。
wget http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt
wc -l hightemp.txt

11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

# コマンドラインで実行してください。
# sedコマンドを利用する場合
cat hightemp.txt | sed s/"\t"/" "/g

# trコマンドを利用する場合
cat hightemp.txt | tr "\t" " "

# expandコマンドを利用する場合
expand hightemp.txt -t 1

# 参考
# sedコマンド: https://www.atmarkit.co.jp/ait/articles/1610/17/news015.html
# trコマンド: https://www.atmarkit.co.jp/ait/articles/1610/03/news017.html
# expandコマンド: https://www.atmarkit.co.jp/ait/articles/1611/02/news023.html

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.

# コマンドラインで実行してください。
cut -f 1 hightemp.txt > col1.txt
cut -f 2 hightemp.txt > col2.txt

# 参考
# cutコマンド: https://www.atmarkit.co.jp/ait/articles/1610/31/news026.html

13. col1.txtとcol2.txtをマージ

12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.

# コマンドラインで実行してください。
paste col1.txt col2.txt > paste.txt

# 参考
# pasteコマンド: https://www.atmarkit.co.jp/ait/articles/1704/07/news018.html

14. 先頭からN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.

# コマンドラインで実行してください。
N=5
head -n $N hightemp.txt

# 参考
# headコマンド: https://www.atmarkit.co.jp/ait/articles/1603/07/news023.html

15. 末尾のN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.

# コマンドラインで実行してください。
N=5
tail -n $N hightemp.txt

# 参考
# tailコマンド: https://www.atmarkit.co.jp/ait/articles/1603/07/news023.html

16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.

# コマンドラインで実行してください。
N=5
split -l $N hightemp.txt

# 参考
# splitコマンド: https://www.atmarkit.co.jp/ait/articles/1711/24/news016.html

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

# コマンドラインで実行してください。
# cutとsortを利用する場合
cut -f 1 hightemp.txt | sort -u

# cutとsortとuniqを利用する場合
cut -f 1 hightemp.txt | sort | uniq

# 参考
# sortコマンド: https://www.atmarkit.co.jp/ait/articles/1611/09/news020.html
# uniqコマンド: https://www.atmarkit.co.jp/ait/articles/1611/14/news021.html

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

# コマンドラインで実行してください。
cat hightemp.txt | sort -k 3 -r

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

# コマンドラインで実行してください。
cut -f 1 hightemp.txt | sort | uniq -c | sort -r

以上です。正解かはわかりません。また、次の章をやったら載せます。

言語処理100本ノック - 第1章: 準備運動

思い立ったので言語処理100本ノックをやっていきます。

www.cl.ecei.tohoku.ac.jp

「第1章: 準備運動」をやりました。テキストや文字列を扱う題材に取り組みながら、プログラミング言語のやや高度なトピックを復習します。

環境

  • python3系
  • Colaboratory上で実施

00. 文字列の逆順

文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

string = "stressed"

print("先頭から: " + string)
print("末尾から: " + string[::-1])

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

string = "パタトクカシーー"
output = string[0] + string[2] + string[4] + string[7]
print(output)

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

string1 = "パトカー"
string2 = "タクシー"
word_list = []

for p, t in zip(string1, string2):
  word_list.append(p)
  word_list.append(t)
output = "".join(word_list)
print(output)

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
output = [w[0] for w in text.split()]
print(output)

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

dic = {}
text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
word_list = text.split()
for i, word in enumerate(word_list):
  w = word.strip(".")
  if i+1 in [1, 5, 6, 7, 8, 9, 15, 16, 19]:
    dic[w[0]] = w
  else:
    dic[w[0:2]] = w
print(dic)

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.

def generate_ngram(sequence, n):
  if isinstance(sequence, str):
    sequence = sequence.replace(" ", "")
  n_gram_list = []  
  for i in range(len(sequence)):
    if i + n <= len(sequence):  
      n_gram_list.append(sequence[i: i+n])
  return n_gram_list

text = "I am an NLPer"
word_list = text.split()
n = 2

# 単語bi-gram
print("単語bi-gram")
print(generate_ngram(word_list, n))

# 文字bi-gram
print("文字bi-gram")
print(generate_ngram(text, n))

06. 集合

"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.

x = "paraparaparadise"
y = "paragraph"

bi_gram_x = generate_ngram(x, n=2)
bi_gram_y = generate_ngram(y, n=2)
print("bi_gram_x: ", set(bi_gram_x))

print("bi_gram_y: ", set(bi_gram_y))

# 和集合
print("和集合: ", set(bi_gram_x) | set(bi_gram_y))

# 積集合
print("積集合: ", set(bi_gram_x) & set(bi_gram_y) )

# 差集合
print("差集合 x - y: ", set(bi_gram_x) - set(bi_gram_y) )
print("差集合 y - x: ", set(bi_gram_y) - set(bi_gram_x) )

# 'se'というbi-gramがXおよびYに含まれるかどうか
print("'se' in bi_gram_x: ", "se" in set(bi_gram_x))
print("'se' in bi_gram_y: ", "se" in set(bi_gram_y))

07. テンプレートによる文生成

引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.

def generate_templete(x, y, z):
  templete_text = "{}時の{}は{}".format(x, y, z)
  return templete_text

x = 12
y = "気温"
z = 22.4
print(generate_templete(x, y, z))

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力 この関数を用い,英語のメッセージを暗号化・復号化せよ.
def cipher(string):
  cipher_list = []
  for s in string:
    if s.islower():
      cipher_list.append(chr(219 - ord(s)))
    else:
      cipher_list.append(s)
  return "".join(cipher_list)

sequence = "It's a little bit funny, this feeling inside. I'm not one of those who can easily hide."
encryption = cipher(sequence)
decryption = cipher(encryption)

print("英語のメッセージ: ", sequence)
print("暗号化: ", encryption)
print("復号化: ", decryption)

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.

def typoglycemia(text):
  typoglycemia_list = []
  word_list = text.split()
  
  for word in word_list:
    if len(word) <= 4:
      typoglycemia_list.append(word)
    else:
      midle = word[1:-1]
      random_midle = "".join(random.sample(midle, len(midle)))
      new_word = word[0] + random_midle + word[-1]
      typoglycemia_list.append(new_word)

  return " ".join(typoglycemia_list)

text = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(typoglycemia(text))

以上です。正解かはわかりません。また、次の章をやったら載せます。

モブプロやってみた。

モブプロをチームで初めて実施しました。やってみた感想とかをこの記事では書きます。

モブプロとは

モブとは「チームで一緒に働くこと」みたいな意味があるらしいです。モブ・プログラミング(モブプロ)は、チーム全員が一緒になって、一つのコードを映した画面を見ながら、議論を行い開発・実装を進めるプログラミング手法です。1人がドライバーとしてPCでタイピングを行い、残りの人は議論・発信をしながらドライバーに対して実装の指示を出します。一定時間でドライバーを交代しながら進めていきます。 ペア・プログラミング(ペアプロ)が2人で進めるのに対して、モブプロは3人以上のメンバーで実施します。

目的

モブプロは、チームのソフトウェア開発力を高めることを目的に実施されます。人によって開発スタイルやモジュールの切り出し方が違ったりしますが、同じ開発ゴールに対してお互いの考えをシェアし議論しながら実装を進めることで、各人が学びを得てチーム全体の開発力底上げに繋げます。特に開発力が低い初期のチームにおいては、他人の開発環境(例えばvimやtmuxの使い方)も知れるので、生産性が高まる様々な学びを得られると思います。

役割

一般的には「ドライバー」と「ナビゲーター」の2つの役割があります。ドライバーはタイピングを行う人、ナビゲーターはドライバーに指示を出す人です。また、開発初期のチームでは、実装経験が低くうまくディスカッションができないことがあるので、チームがうまく学びを得ることができるように、開発力が高いエンジニアを「ファシリテータ」にすることがあります。私たちのチームでも、スーパーエンジニアにファシリテータの役割を担ってもらい進めました。

  1. ドライバー(タイピスト

    • PCでドライバーの指示通りにタイピングを行う。
    • 自分の意思で実装は行わない。
    • 自分が実装しないような指示に対しても躊躇なく実装を進める。
    • 20分程度で、他のナビゲータと交代する。
  2. ナビゲーター

    • ナビゲーター同士でコミュニケーションを取りながら、ドライバーに実装の指示を出す。
    • お互い傾聴を忘れずに実施する。
  3. ファシリテータ

    • チームが学びを得られるように、チームの議論を支援する。

準備するもの

  • 全員が平等に見れる大きな画面
  • PC1台(ドライバーが交代しながらタイピングをする用)
  • 全員が利用できるエディタ
  • コーディングしながら確認できるタイマー
  • 議論ができるホワイトボード
  • リラックスできる何か

心がけること

  • 参加者全員が、モブプロに集中する。
  • 人を批判しない。コードを批判する。
  • 技術的負債の実装者を批判しない。負債を作らなくてはいけなかった状況を批判する。
  • 傾聴の姿勢

進め方

  • 最初に開発内容とゴールについて説明する。
  • チームで実装方針について、ホワイトボードで議論しながら設計を行う。
  • 最初のドライバーを決める。
  • ナビゲータ同士が議論をしながら、一つの画面を見ながらドライバーに指示を出しつつ実装を進める。
  • 20分でドライバーを交代することを繰り返す。
  • 1時間半くらいで休憩を入れる。
  • 実装が完了したら、最後に振り返りを行う。

やってみた感想

  • まず、開発力が低いチームだと、そもそも何から手を付けてよいかわからず、うまく議論ができない。ファシリテーターファシリテーションを行ってもらうことをおススメする。
  • 議論が空中戦になり実装が進まないことが出てくる。実装を進める中で新たに見えてくる・理解が進むことがあるので、空中戦に終始せず、とにかくコードを書くことを意識する。
  • 開発スタイル(vimとかpep8とかpyflakesとかgitコマンドとか)で、開発初期のメンバーは学びを得ることができる。初期ほど効果あり。
  • 書籍を読んでも理解が難しい、抽象化や継承や共通化などを、コードの実装を進めながら深く理解できる。
  • 技術的な知識を得られる。
  • コードの品質を上げることができる。
  • 自分が開発に携わっていない他者のタスクについても、チーム全体で理解が深まる。
  • チームは一体感を得る、チームビルディングの観点でもよい。
  • めっちゃ疲れる。でも楽しい。

個人的には、チーム全員が参加するので、モブプロを実施するコストは高いですし、ペアプロ同様結構疲れます。でも、モブプロを数回行うことでチームの開発力や実装における生産性は大きく向上すると思いますので、是非チームの初期段階で実施するべきだと思います。

以上です。

学習済みEmbeddingを利用する時の前処理ゴールデンルール

Word2vecやfastText、Gloveなど、Word Embeddingの方法は広く普及してきており、外部から学習済みのEmbeddingデータをインポートし、そのベクトルを手元のデータセットに適用し利用するケースも増えています。

学習済みEmbeddingを効果的に利用するためには、一般的な自然言語の前処理とは異なるアプローチが必要らしいです。次のKernelでは、ゴールデンルールとして紹介されていますので、このブログで触れたいと思います。

How to: Preprocessing when using embeddings | Kaggle

目次

そもそもEmbeddingとは

こちらをご覧ください。

ishitonton.hatenablog.com

2つのゴールデンルール

次の2点が前処理の方針です。

  1. 学習済みEmbeddingを利用する際には、ステミングやストップワードの除去等の標準的な前処理ステップを利用しない。
  2. 自データセットのvocabularyをできるだけ、Embedding側に近づけるように処理を行っていく。

ここでの注意点としては、利用する学習済みEmbedding毎に、Embedding作成までのプロセス(前処理や除外ルール)は異なる点です。つまり、このEmbeddingに近づけるための作業は画一的な手順ではなく、利用するEmbeddingと対話をしながら、ある程度探索的なデータ分析プロセスを挟むことになります。

利用するデータセット

用意するのは、次の2つです。

  1. 学習済みEmbeddingデータを適用するデータセット
  2. 学習済みEmbeddingデータ

前者には、Kernel同様KaggleのQuora Insincere Questions Classificationコンペのデータセット(trainデータ)を利用します。

Quora Insincere Questions Classification | Kaggle

後者には、GoogleNewsをもとに作成されたWord2vecのEmbeddingを利用します。次のリンクからダウンロード可能です(約2GB)。

GoogleNews-vectors-negative300 | Kaggle

適用先のデータセット

Quoraは実名制のQ&Aサイトで、データセットには質問のテキストデータが含まれています。

text.head()
0    How did Quebec nationalists see their province...
1    Do you have an adopted dog, how would you enco...
2    Why does velocity affect time? Does velocity a...
3    How did Otto von Guericke used the Magdeburg h...
4    Can I convert montra helicon D to a mountain b...
Name: question_text, dtype: object

データセットのVocabularyを作成

テキストデータからVocabularyの辞書を作成していきます。

def build_vocab(sentences, verbose =  True):
    vocab = {}
    for sentence in sentences:
        for word in sentence:
            try:
                vocab[word] += 1
            except KeyError:
                vocab[word] = 1
    return vocab

sentences = text.apply(lambda x: x.split()).values
vocab = build_vocab(sentences)
print(vocab)
{'How': 261930,
 'did': 33489,
 'Quebec': 97,
 'nationalists': 91,
 'see': 9003,
 'their': 34810,
...

学習済みEmbeddingの読み込み

gensimを利用して、Word2Vecの学習済みEmbeddingを読み込みます。

from gensim.models import KeyedVectors

news_path = 'GoogleNews-vectors-negative300.bin'
embeddings_index = KeyedVectors.load_word2vec_format(news_path, binary=True)

試しにjapanという単語をみてみます。

embeddings_index["japan"]
array([-3.22265625e-01,  2.30712891e-02,  1.77734375e-01,  3.35937500e-01,
       -2.75390625e-01, -3.01513672e-02, -2.11914062e-01, -2.98828125e-01,
        3.76953125e-01, -1.13281250e-01, -6.54296875e-02, -3.53515625e-01,
       ...
       -6.95312500e-01,  2.50000000e-01,  7.66601562e-02,  2.02148438e-01],
      dtype=float32)
print(embeddings_index["japan"])
300

このEmbeddingは、300次元からなるベクトル表現であることがわかります。

vocabと外部Embeddingの単語の重複チェック

Out of Vocabulary(OoV)はすなわち知らない単語のことを指します。データセットと外部の学習済みEmbeddingには、存在する単語に差異があります。外部Embedding(Google News)には存在しないが、データセットのVocabularyには存在する単語がOoVです。これを利用することで、前処理を改善することが可能です。

以下は、vocabとembeddingとの間の共通部分およびOoVをチェックする関数です。

import operator 
from tqdm import tqdm

def check_coverage(vocab, embeddings_index):
    a = {}
    oov = {}
    k = 0
    i = 0
    for word in tqdm(vocab):
        try:
            a[word] = embeddings_index[word]
            k += vocab[word]
        except:

            oov[word] = vocab[word]
            i += vocab[word]
            pass

    print('Found embeddings for {:.2%} of vocab'.format(len(a) / len(vocab)))
    print('Found embeddings for  {:.2%} of all text'.format(k / (k + i)))
    sorted_x = sorted(oov.items(), key=operator.itemgetter(1))[::-1]

    return sorted_x

実行します。

oov = check_coverage(vocab, embeddings_index)
Found embeddings for 24.31% of vocab
Found embeddings for  78.75% of all text

出力結果からは、Quoraのテキストデータから得られた単語の24.31%しか、外部Embeddingデータに含まれていないことがわかります。これでは、外部のEmbeddingを利用して、テキストデータに内包される情報を効果的に表現することができません。データクレンジングを通じて、できるだけこの割合を高めていきます。(=OoVを減らしていく。)

OoVの出力し、改善の糸口を探る

OoVを出力してみます。

oov[:20]
[('to', 403183),
 ('a', 402682),
 ('of', 330825),
 ('and', 251973),
 ('India?', 16384),
 ('it?', 12900),
 ('do?', 8753),
 ('life?', 7753),
 ('you?', 6295),
 ('me?', 6202),
 ('them?', 6140),
 ('time?', 5716),
 ('world?', 5386),
 ('people?', 4971),
 ('why?', 4943),
 ('Quora?', 4655),
 ('10', 4591),
 ('like?', 4487),
 ('for?', 4450),
 ('work?', 4206)]

サンプル出力からいくつかのことがわかります。

  • 語尾に?がついたVocabがEmbeddingには存在していない。?とか記号分割の処理をいい感じにすればOoVを減らせる。
  • atoofandGoogle newsのEmbeddingには存在しない。学習時に削除している??

得られた仮説をもとに、OoVを減らせるかトライしてみます。

前処理1(記号の処理をいい感じにする)

記号は削除すべきか・それとも残すべきかは、場合によります。ゴールデンルールに従えば、利用する外部Embeddingの単語として存在する場合には残せばよいし、存在しない場合は削除した方がよいです。データセットのVocabをできるだけ外部Embedddingの単語に近づけていきます。

今回のデータで?&をについて確認してみます。次の結果からは、?についてはVocabの単語から削除し、&については外部Embeddingに存在するので残しておくべきということになります。これらは利用する外部Embeddingデータによって前処理は異なるため、判断は変わってきます。

'?' in embeddings_index
False
'&' in embeddings_index
True

そんなんで、クレンジングしていきます。&は語彙として残しますが、他は削除します。

def clean_text(x):
    x = str(x)
    for punct in "/-'":
        x = x.replace(punct, ' ')
    for punct in '&':
        x = x.replace(punct, f' {punct} ')
    for punct in '?!.,"#$%\'()*+-/:;<=>@[\\]^_`{|}~' + '“”’':
        x = x.replace(punct, '')
    return x

text_clean = text.progress_apply(lambda x: clean_text(x))
sentences = text_clean.apply(lambda x: x.split())
vocab = build_vocab(sentences)
oov = check_coverage(vocab, embeddings_index)
Found embeddings for 57.38% of vocab
Found embeddings for  89.99% of all text

Quoraのテキストデータから得られた単語の57.38%が、外部Embeddingデータにも含まれるようになりました。前回の24.31%から大きく増加しました! さらに、再度OoVをチェックしてみます。

oov[:20]
[('to', 406298),
 ('a', 403852),
 ('of', 332964),
 ('and', 254081),
 ('2017', 8781),
 ('2018', 7373),
 ('10', 6642),
 ('12', 3694),
 ('20', 2942),
 ('100', 2883),
 ('15', 2762),
 ('12th', 2551),
 ('11', 2356),
 ('30', 2163),
 ('18', 2066),
 ('50', 1993),
 ('16', 1589),
 ('14', 1533),
 ('17', 1505),
 ('13', 1390)]

語尾に?がついた単語については先ほどの前処理によってなくなりましたが、新たな仮説が導かれます。

  • OoVに数字がたくさんある。Google news側で、数字に対するなにかしらの前処理が行われているのでは?

前処理2(数字の処理をいい感じにする)

今回も、データセットのVocabをできるだけ外部Embedddingの単語に近づけていきます。そのためにEmbedding側でどのように数字が処理されているかを探索的に探っていきます。 結論としては、利用したGoogle newsのEmbeddingでは、数字は#に置き換えられているようです。10であれば##99.99であれば##.##といった具合です。

データセット側も同様の処理を行います。

import re

def clean_numbers(x):
    x = re.sub('[0-9]{5,}', '#####', x)
    x = re.sub('[0-9]{4}', '####', x)
    x = re.sub('[0-9]{3}', '###', x)
    x = re.sub('[0-9]{2}', '##', x)
    return x

text_clean2 = text_clean.progress_apply(lambda x: clean_numbers(x))
sentences = text_clean2.progress_apply(lambda x: x.split())
vocab = build_vocab(sentences)
oov = check_coverage(vocab, embeddings_index)
Found embeddings for 60.41% of vocab
Found embeddings for  90.75% of all text

さらに、60.41%と改善しました!

こんな感じで、前処理をやっていきます。

そのほか前処理していること

この他にも、OoVをもとにEmbeddingに近づけるためのアイデアを探っていきます。割愛しますが、Kernekでは以下のような処理も行っています。

  • QuoraのQ6Aのテキストデータには、ソーシャルメディアならではの人間が間違いやすいスペルミスが存在している(おそらくGoogle news側では少ない)。これをいい感じにしてやる。
  • OoVに多い「a」、「to」、「and」、「of」という単語を削除する。Google newsのEmbeddingを学習するときには明らかにこれらの単語をダウンサンプリングしている。

最後に

なんか間違ってたり、よりよいアイデアあれば教えてください。