Python
自然言語処理
機械学習
MachineLearning
初心者向け

身近なデータで機械学習 〜人から褒められるために機械学習を使ってみた〜

はじめに

私は普段の業務に加えて、機械学習の勉強をしています。しかし勉強をしている中で、取り組んでいるデータセットに対して、様々な解析をしてみよう、試行錯誤しよう、と思えないことがあります。その理由の一つに自分の興味があるデータを使わないと真剣に取り組めないことがありました。

例えば、少なくとも私の生活の中では、0から9まで手書きで書かれた画像を分類識別する機会も、3種類のアヤメを形態データから識別する機会もありません。このようなデータセットを使って学習していても、なかなか身が入らず、やり込もうという気になれませんでした。同じようなことを感じる方も、少なくないのではないでしょうか。

そこでこの記事では、身近なデータセットを使った例として、弊社にある「良いこと共有」という制度から得られたデータセットを使って、解析をしてみた結果をお伝えします。
※この記事の出た段階ではまだまだ途中段階であり、前処理の方法や採用するアルゴリズム・パラメータはさらに改良していく予定です。

褒められたい

突然ですが皆さん、「人から褒められたい」と思いませんか?
褒められることが苦手という方も多くいらっしゃると思いますが(私もどちらかといえばこちら側ですが)、どうせなら褒められないよりは褒められたほうが嬉しいですよね。

実は褒められることは心理的にポジティブなだけでなく、身体的にもポジティブな結果をもたらすことが報告されています(例えばこちらの記事をご覧ください)
これはどんどん褒められるしかないですね。

ラクして褒められたい

しかし一方で、褒められることはなかなか難しくもあります。やみくもに褒められようと行動しても、なかなか実を結ばないこともあるでしょう。

例えばあなたは社内の誰かから褒められようと思って、毎日みんなにお茶を入れてあげたり、肩をもんであげたりするかもしれません。
ochakumi_man.png

でもひょっとしたら、それは無駄な行動かもしれません。なぜなら

  • 仕事とは関係ないから
  • エンジニアらしいtechな内容じゃないから
  • 周りは全員肩こり知らずのコーヒー好きだから

つまり「どんな行動を取れば褒めてもらえるか」が分からなければ、効率的に、すなわちラクに褒めてもらうのは難しいということです。

他の人は何して褒められてる?

ではどうすれば褒められる行動が分かるでしょうか。色々方法はあると思いますが、一番ストレートなのは「他の人がどんなことをして褒められているかを知る」ことでしょう。
しかしそれを知るにはなかなかハードルが高そうです。

  • 周りの人に聞いて回りましょうか?かわいそうな人を見る目で見られるでしょう。とても耐えられそうにありません。
  • 周りの人をよ〜く観察しましょうか?肝心の仕事が手につかず、逆に上司に怒られる未来が見えます。
  • ググってみましょうか?世間一般で褒められる行動の例は得られそうです。しかしそれが自分の会社にも適用できるかどうかはわかりません。前述のように結局お茶を入れまくるだけになるかもしれません。

そう、普通はそんな都合よく知ることはできません。しかし、幸運にも弊社にはそれを知ることのできる方法が存在したのです。それが「良いこと共有」と言う制度です。

「良いこと共有」という制度

この制度は、業務中など日々の生活の中で、社員同士に対して「この行動はよかった」「助かった、ありがとう」と思えることを見たり体験した時に記入してもらうことで、社員全員に共有しようという制度です。共有内容はGoogle Spreadsheetに記載され、全社員が見ることができます。
毎月共有内容の中から最優秀賞も選ばれており、2017年に始まった制度ですが社内に広く浸透している印象を受けています。
例えば以下のような内容で投稿されています。

  • 現場内のメンバー向けにNW勉強会を開催して頂いた。資料も問題形式に作っており、ただ聞くだけではない形になっており、とても良かった。
  • 運用業務のフォローに入っていただいた。設計構築業務も対応する中で運用業務も気にしていただいてくれて大変助かった。
  • フットサル部部長として、部を盛り上げるためにいろいろ企画してくれた。

これはまとまったデータを期待できそうです。これをうまく使えれば、楽して褒められることも夢ではないかもしれません。

方法

実施内容の流れ

  • 今回は以下のような流れで解析を行いました。 ML-flow.jpg
  1. まず機械学習を使う目的を決めます。
  2. 次に使用するデータの準備とデータの全体像の観察を行います。
  3. データの前処理を行います。
  4. 処理を走らせモデルを構築します。
  5. 処理結果を見て、問題があればパラメータ等調整して再び処理を走らせます。

このうち今回は特に2と4に焦点を当てて紹介します。

使用データの概要紹介

使用するデータの中身はこのようになっています。

# データ数
投稿総数:1539

# 投稿期間
2017/01/05 - 2018/08/31

# データカラム
投稿日時、メールアドレス、入力者氏名、入力者所属プロジェクト、内容(200文字まで)、対象者氏名、対象者所属プロジェクト

# 1投稿あたりの文字数
69.90667530784187 # average
0 # minimum
200 # maximum

おそらく誤入力であろう空メッセージが登録されていましたので、前処理ではこの辺りを削除しました。

トピックモデル

前処理を行った後、データ全体としてどのような傾向があるのかを探ろうと考えました。そのためにトピックモデルという手法を用いて文章群を分類することを考えました。
トピックモデルの詳細については既に多くの素晴らしい記事がありますので説明はしませんが、「文章中に現れる単語の出現頻度を確率として算出し、推定するモデル」となります(詳しくはこちらの記事をどうぞ)。
今回は以下のライブラリを使用しました。

  • janome
    形態素解析を行うライブラリとして、MeCabと並んで有名なものです。MeCabと比べて処理速度は1/10程度と言われますが、一方でjanomeはpipコマンド一発で導入できる手軽さがあると思っています。今回は文章量もそれほど多くないため、手軽に導入できるjanomeを採用しました。
    使い方等はこちらからどうぞ

  • gensim
    トピックモデル生成ライブラリとして有名なものの一つです。非常にスタンダードなライブラリであり、使用方法も簡単なことから、今回採用しました。
    使い方等はこちらからどうぞ

パラメータは以下のように設定しました。実際に使用したコードをこちらに載せますが、20行程度しかなく、簡単にできます。もちろんもっと綺麗には書けるはずですが。。。

word_gensim.py
import csv
from janome.tokenizer import Tokenizer
from collections import Counter
import gensim
from gensim import corpora

raw_data = open("yoikoto_data.csv", "r", encoding="utf-8")
raw_reader = csv.reader(raw_data, delimiter=",")
raw_list = list(raw_reader)

word_all = []
for raw_num in range(len(raw_list)):
   t = Tokenizer()
   word = t.tokenize(raw_list[raw_num][5])
   word_all.append([token.base_form for token in word if token.part_of_speech.split(',')[0] == u'名詞'])
# 名詞のみを用いる

dictionary = corpora.Dictionary(word_all)

dictionary.filter_extremes(no_below=2, no_above=0.05)
# no_below:指定回数以下の出現単語を削除
# no_above:指定パーセント以上の出現頻度の単語を削除

dictionary.save_as_text('./dictionary_word_20181008-1.txt')

corpus = [dictionary.doc2bow(cor_num) for cor_num in word_all]
corpora.MmCorpus.serialize('corpus_word_20181008-1.txt', corpus)

dictionary = gensim.corpora.Dictionary.load_from_text('dictionary_word_20181008-1.txt')
corpus = corpora.MmCorpus('corpus_word_20181008-1.txt')

lda = gensim.models.ldamodel.LdaModel(corpus=corpus, num_topics=5, id2word=dictionary)
# num_topics:分割するトピック数

print(lda.show_topics())

raw_data.close()

文章生成(RNN)

後ほどトピックモデルの結果を紹介しますが、単語に基づいて文章を分類するだけでは、十分とは言えませんでした。それは、単語レベルだと具体的な行動パターンに落とし込めないためです。  
例えば「配属」「質問」の単語が含まれている場合、複数の行動パターンが想像できます。

  • プロジェクトに新しく配属された人に質問する
  • プロジェクトに新しく配属された人からの質問に答える
  • そのプロジェクトに配属した人に質問をする ...

これではいろいろと想像ができてしまい、具体的にどんな行動をすれば良いかがわかりませんでした。この問題を解消しようと、文章レベルでの行動パターンが提案できれば良いのではと考えました。そのために採用したのがRNN(LSTM)になります。

RNN(Recurrent Neural Network)とはDeep Neural Networkの1種であり、インプット/アウトプットのデータに可変長の連続的なデータを使用することができるモデルになります。そのため、時系列データや文章などのデータセットに適したモデルになります。こちらの記事に詳しい説明が書いてありますので、詳細はこちらをご覧ください。

今回はgithubにあるこちらのソースコードを利用いたしました。
github.com/sherjilozair/char-rnn-tensorflow

このコードを公開しているSherjil Ozairさんは、有名なGANの論文で共著者として名を連ねている研究者の方です。このような方の書いたコードが公開されていて利用できるというのは、本当に素晴らしいですね。

パラメータは以下の項目を調整しました。

--model: RNNの手法の選択(今回は全てLSTMで実施)
--rnn_size: RNNの隠れ層の状態のサイズ
--num_layers: 隠れ層の層数
--num_epochs: 学習の試行回数(エポック数)

実行環境

以下の2種類の環境で実行しました。

  • トピックモデル: ローカル環境(Mac)
  • 文章生成: Google Colaboratory(GPU)

結果

トピックモデル

実行結果を載せます。いろいろトピック数を変更した結果、k=5とk=10でそれなりの結果が現れたので、それら2つの結果を載せます。
なお、表の縦軸はそれぞれ異なるトピックを、横軸は各トピックで出現確率の高い単語となります。単語◯は◯の数字が小さいほど出現確率が高い単語となります。

k=5

単語1 単語2 単語3 単語4 単語5
トピック1 作業 参入 姿 説明 丁寧
トピック2 ところ メール 確認 フォロー
トピック3 作業 内容 お願い 参加
トピック4 参加 研修 勉強 資料
トピック5 1 時間 資料 丁寧 ため

眺めてみると、特にトピック1、2、4が特徴的な単語が並んでいるように見えます。
ここから想像できる行動パターンだと、例えば以下のものが想像できます。

  • (新規)参入者向けに説明する
  • 研修に参加して勉強する

k=10

単語1 単語2 単語3 単語4 単語5
トピック1 勉強 担当 資料 参加 発表
トピック2 作業 ( 自身 お願い
トピック3 質問 作業 環境 情報 配属
トピック4 非常 時間 参入 ところ
トピック5 資料 研修
トピック6 フォロー 調整 参加 実施
トピック7 1 お願い 参加 参入 時間
トピック8 おかげ 資料 目標 発言
トピック9 丁寧 情報 勉強 環境 参加
トピック10 作業 ところ 状況 報告

当然ですがk=5の時には出てこなかった単語が登場し、少し異なるトピックが登場します。特にトピック6、8あたりは特徴的かと思います。
例えば以下のような行動パターンが想像できます。

  • (勉強会などを)実施、参加者の調整を行った
  • (達成)目標などに関して発言した

一方で明らかにトピック分割に関係のなさそうな単語(方、等、おかげなど)も出現します。この辺りはトピック数に依存するのか、あるいはパラメータの調整で取り除けるかもしれませんし、改善できるポイントだと考えています。

文章生成

実行結果を載せます。ダメだったものも併せて紹介します。

生成結果(ダメ)

# ダメな例のパラメータ
python train.py --model=lstm --rnn_size=128 --num_layers=2 --num_epochs=50

# 出力結果
い内スムウ入頂り、お変いそれげ、でらけをさしてくださりました。
席れぞれ作換していました。
ありがたう、大変れて」とこと思いました

過【月的なができます!ごとう、A自身も共有して話&から色度にありがます。(と出してわりを休頃さに伝って発かげたことの後えらお願いされてありがとうございました。

社末の打んな要に聞きあうがとうございます。
個経問がありがとうございました。
慣れ様成さりた。ざい、業ォロワーの前に顔も細かずとを同して業ォローをへの参入しましなが、助体ドに議側だいてくれてこれ長をわらしていたもように、改めて、量属リチーフババグラベーを緊況にあり、とてもらかります。

朝礼有、お疲りしく直認を見て多いて理くしていました。時間方に卒ぎ内容に自身をフーウさんをの3M卒遅つマぎなの準在を感しないて、仕業推リスで深

業カy揮理ありがとうございました。今前が,場れるサケートを技C案件にかっても地道に別を作なり姿をして、必見よかました前のじいこと勉術調褒のしな業務マスャンかっていた。ありがとたございます。
年配容の議人に挙っておいている中とめまんで一答したくせたい交属作成したど感

・・・いかがでしょうか。これは実に難解な日本語ですね。
「ありがとうございます」など所々では何とか読めますが、ざっと見る限りでは「元データを元に出力されたランダムな文字出力の結果」としか言えなさそうです。。。

生成結果(よさげ)

# 良さげな例のパラメータ
python train.py --model=lstm --rnn_size=1024 --num_layers=2 --num_epochs=50

# 出力結果
なり、安心感を持って対応しているにもかかわらず、運搬合力で短納期で多忙にも関わらず教えて下さいました。社内から実施向け締められました、とても良い事だと思います。
自日も見えてきて、ANCの勉強会を取りまとめて、他のメンバーに展開頂きありがとうございます。

OJTも積極的に私自身の後、検証を巻き取って頂きありがとうございました。
みんなの私自身も向上心」とご自身余裕がないのではなくわからない内容について、変わずに皆さんも作業までどれてくださいました。

当期の席まで用意し不明点せなさせてもらって中のお陰で大変であったと動も刺激を受けた上で、1山気配りがほぐれている時はなく大変だと思当までできるのか残っていきましょう。

先ほどのダメな結果と比べて、かなり日本語の文章っぽいものが出力されるようになりました。参考になりそうなものもちらほら現れています。

  • 検証を巻き取っていただきありがとうございます。
  • 短納期で多忙にもかかわらず教えてくださいました。

...他の人の仕事を代わりに引き受けたり、忙しい中でも親切に振る舞うことで、褒められる可能性はかなり上がりそうです。言われてみれば確かにそう感じますね!

生成結果(もっと良さげ。。。?)

さらにエポック数やレイヤー数を増やしたりすると、さらに良さげな結果も出てきます。
例えば以下のような具体的な内容の文章も出現するようになりました。

  • 人狼イベントでは準備や人集めにご協力頂きありがとうございました!
  • わからないことがないか、常に相談に乗ってくださってありがとうございます。
  • 私がサーバー構築の自動化を推進しているとき、「今は自動化に注力してほしい」と他の仕事を引き受けていただけました。

ただこれらの文章、実は元データと全く同じ文章なのです。できればこのような、元データと全く同じ文章というものは出力しないようにしたいのですが。。。
日本語として正しく、かつ元データとは異なる文章を出力するにはどうすればいいか、今後の課題として取り組んでいきたいと思います。

結論

どうやったら(弊社で)褒められるか?例えば以下のような行動をとれば良さそうです。

  • 新規参入者の方をサポートする
  • 研修に参加して勉強し、内容を共有する
  • 業務をサポートする
  • 忙しい時でも丁寧に対応する

めちゃくちゃ当たり前のことしかないですね。。。要は当たり前のことを続けていこう、ということですか。。。楽に褒められたいと言う願望を叶えるのは、今の所かなり難しそうです。
tobotobo_aruku_woman.png

その他

やってみた感想

今回は偶然身近にあったデータセットを使って、機械学習にトライしてみました。やはり身近なデータセットの方が試行錯誤にかける時間が長くなり、一定の効果はあるのではないかと思います。
特にパラメータ調整は、少しずつパラメータを変更してその結果を確認するという、とても地道な作業となります。そのため興味の薄いデータセットだと、ただ推測結果を見て「前回より向上したか、しなかったか」だけを気にしており、楽しいと感じることができませんでした。しかし身近なデータセットを使うと、結果が出てくるたびに確認するのが楽しくなり、暇があればパラメータ調整をしていました。身近なデータ、自分の興味関心のあるデータを使うことは、モチベーションを保つ上で大事であることを感じました。

発表しました

弊社で定期的に実施しているAP TECH FESTという社内イベントで本内容を発表しました。完全にネタ勝負でしたが、社内で身近なテーマということもあり、そこそこ受けたようなので安心しました。。。