こんにちは!MLエンジニアの野澤(@takapy0210)です。
今更ですが東京喰種トーキョーグールというアニメを最近見始めました。
内容はもちろん面白いのですが、OPの歌い出し「教えて 教えてよ 〜」部分の声質がとても印象的で、どうにか真似できないかと練習しているところです。
さて、今回はこちらの勉強会でもLTさせていただいた「機械学習と人が協力してコミュニティを支えているよ」という話をしようと思います。
目次
下記が資料です。 以降、スライド内容を抜粋しながら進めていきます。 ママリとは、全国のママがお互いに悩みを相談し合うことのできるQ&Aサービスです。 真摯な悩みを投稿するユーザが多くいる一方で、下記のように不適切な投稿をする人がいるのも、また事実です。
このような中でコミュニティを健全な状態に保つためには、不適切な投稿は運営側で検知し、然るべき対応を行う必要があります。しかし、1日の投稿数が数千件を超える中で、全てを人間が目視チェックするのは現実的ではありません。 そこでママリでは、不適切な投稿を検閲するために機械学習を用いています。
現在の検閲フローでは、機械学習モデル(以降、モデル)が「不適切な確率50%以上」と推論した投稿に関して、人間が目視チェックするようになっています。
なぜ、モデルの推論結果のみで処理せず、最終的に人間がチェックする必要があるのでしょうか。 今回の例に出したような「簡単に稼げる方法教えますよ」等の質問であれば、わざわざ人間がチェックする必要もないと思うのですが、例えば下記のような質問はどうでしょう。 この質問では「簡単に稼げる方法教えますよ」という意味の一文が入っているものの、実際は不適切な投稿ではなく、ユーザが抱えている悩みであることが見て取れると思います。 このような投稿をモデルで推論したときに、「誰でも簡単に稼げます!安心安全!今なら初期費用ゼロです!⭐未経験者歓迎⭐」という部分の影響で、不適切な投稿として判断されてしまう可能性があります。 そのため、冒頭でも述べた通り「不適切な確率50%以上と推論された投稿については、人間の目視チェックを通して対応する」というフローになっているわけです。 こうすることで、50%未満と推論された投稿については、人間の目視チェックが不要となり(=コスト削減)、機械学習が苦手とする微妙なニュアンスをもった投稿の対応に人間が注力できるようになります。 そして上記のように、機械学習と人それぞれの良いところを生かし合うことで、コストを削減しつつ、コミュニティを健全な状態に保つことが可能になっている、というわけです。 現在運用しているモデルについても少し触れたいと思います。 ママリ内には多くのテキストデータが存在しており、そのデータをコーパスとしてgensimを用いて単語の分散表現を計算しています。 下記に、gensim.word2vecを用いて言語モデルを作成するコード例を載せておきます。 この言語モデルは、例えば、単語の類似度を測ることが可能だったりします。 「お互い」という単語が出てくるあたり、コミュニティサービスから作ったモデルならではに感じますね。 そして、上記で得られた単語の分散表現をネットワークの入力として、双方向のLSTMを用いてモデルを構築しています。
下記は実際にモデル構築に使ったコードの一部です。 先にも述べた通り、現在ママリでは1日に数千件の質問が投稿され、学習に使えるデータは日に日に増加しています。 しかし現在は、このモデル更新を全て手動で実施しているため、「機械学習基盤イケてるでしょ?」とは言い辛い状態です。(とはいえ、全てAWSのサービス内で完結しているため、手順フローを踏襲すれば誰でも/いつでも更新できる状態ではあります) 今後はこの手順を自動化すべく、AWSの各種サービスを組み合わせながら、自動更新フローを構築していく予定です。 今回はママリでの機械学習活用事例についてお話しました。 機械学習は、解決したい課題によってはとてもインパクトのあるものになり得ますが、課題の種類によっては、機械学習をフル活用するよりも、人間と機械学習お互いの長所を活かし合うことが大切だと考えています。 今後も、機械学習を使うことを目的とするのではなく、あくまでも課題解決の手段として活かせる部分には存分に活かしサービスグロースに貢献していければと思っています! すみません、最後にちょっと告知させてください...! 今月末に開催されるAWSの下記イベントで登壇する予定です。 また、すでに満席となってしまっていますが、下記イベントでもLTする予定ですので、参加者の皆さまよろしくお願いします!LT資料
ママリ内での課題
ユーザの熱量がとても高く、運営としてもコミュニティの雰囲気をとても大切にしています。機械学習と人が協力しているってどういうこと?
モデルについて
word2vecのオプションについてはこちらに分かりやすく掲載されているので、気になる方は見てみてください。from gensim.models import word2vec
import multiprocessing
cpu_count = multiprocessing.cpu_count() # CPUのコア数を取得する
corpus = word2vec.LineSentence('corpus.txt') # コーパスの読み込み
# 学習
model = word2vec.Word2Vec(corpus,
size=200,
window=5,
hs=1,
min_count=5,
sg=1,
iter=5,
workers=cpu_count
)
# モデルの保存
model.save('w2v.model')
試しに、ママリのコーパスで学習させたモデルに対して、「夫」の類似単語TOP10を出力してみます。# 夫 と類似している単語
model.wv.most_similar(positive=['夫'])
>>>
('主人', 0.9211037158966064),
('旦那', 0.8511199951171875),
('旦那さん', 0.7135452032089233),
('ダンナ', 0.6880300641059875),
('私', 0.6779747009277344),
('妻', 0.642198920249939),
('実父', 0.6189703941345215),
('わたし', 0.6182470321655273),
('父', 0.6118142604827881),
('お互い', 0.5914695262908936)
ネットワーク自体はシンプルな構成になっており、出力層の活性化関数にsigmoid
を用いることで確率を出力しています。
また、emb_matrix
に上記で学習させた単語の分散表現を渡すことで、ネットワークのEmbedding層に初期設定しています。# モデルの一部
def build_model(emb_matrix: np.ndarray, input_length: int):
model = Sequential()
model.add(Embedding(
input_dim=emb_matrix.shape[0],
output_dim=emb_matrix.shape[1],
input_length=input_length,
weights=[emb_matrix],
trainable=False))
model.add(Bidirectional(LSTM(64, recurrent_dropout=0.15)))
model.add(Dropout(0.25))
model.add(Dense(64))
model.add(Dropout(0.3))
model.add(Dense(1, activation='sigmoid'))
return model
今後取り組んでいくこと
このような場合、最新のデータでモデルを定期的に更新することは、モデルの精度を維持・向上させるためにとても重要だと考えています。最後に
最後の最後に(告知)
当日はコミュニティサービスにおいて、どのようにAWS×自然言語処理を活用しているかというお話する予定です。
ご興味のある方いましたら、ご参加お待ちしております!
ml-loft.connpass.com
globis.connpass.com