T5 (てゆうか超手軽に強いラッパー作れるんですけど)
本年(度) もいつものやつ、やっていきたいと思います。
昨年(度) はこんなのを作りました。
本年(度) はどうしようかということで、顧問HIPHOP アドバイザーのkenichi 氏との企画会議にて、 HIPHOP には「リアルかフェイクか」という議論があることを教わりました。
確かに調べてみると、様々な考え、解釈がありそうです。
- 俺なりのHIPHOP論
- 「ヒップホップにリアルもクソもない。」ラッパーANARCHY × ダンサーKEITAが対談 | | Dews (デュース)
- Creepy nutsはHIPHOPではないのか?〜KREVAが壊しきれなかった壁〜|ショーペンログ
ヘッズの横好きの域を出ない私にとって、この判断はなかなか難しいものです。
どうすればリアルかどうかを判断できるのでしょうか?
機械学習を活用したいところですが、判断基準が不明なため教師データが用意できず、二値分類(リアル or not) は使えません。
一方、昨今発展の目覚ましい自然言語処理の領域では、AI (機械学習による言語モデル) が文章を生成する精度が飛躍的に向上しています。少し前にはGitHub Copilot がプログラムを自動生成することで話題になりました。
これを応用します。
すなわち、「リアルなラッパーが言いそうなこと言ってるやつはリアル」です。
起点となる「リアルなラッパー」はどうしても選定が必要となるため、kenichi 氏に「この人は文句なし」と思わしきラッパーを選定していただき、そのリリックを学習データとして利用することとしました。
そしてこの怪物級のラッパーたちを超合成して出来上がったのが... コイツだッッ!!
怪獣にも「生き様」がある。それが垣間見えるバースを返してくれます。
最後は槍で攻められたことを逆手に取って、投げやりな態度を取っている様子が伺えます。うーん、リアル。
みなさんも大吉が出るまで何度もおみくじを引いてないで、MC キメラのバースと自分の予想を見比べて、自分の「リアル」を占ってみてはいかがでしょうか?
※ 1回目のメッセージは寝ていることがあります。
@721qnjke
MC キメラのつくり方
ソースはこちらです。
データ
こちらです。
MCキメラ 学習用データ - Google スプレッドシート
train するときの中身はこんなかんじです。最後の数字はrapper_id です。分類タスクを試したときに利用しました。
❯ head train.tsv 革命家でrapの松尾芭蕉 かましてるwavy毎日fosho woooこりゃ荒波 マジな話でこのremixはヤバすぎるlook look look 8 そこは情報過多な競争社会 俺が居なくなりゃ頂上が無いも同然 俺はここらじゃイケてるほう ol、ショウガールにモデルも 2 まだ駆け出してギャラは三万円 ライブの後に消えるシャンパンで あなたの才能は特別よ 君の言葉だけを信じれたよ 6 this is no flavor syndrome お前らじゃ味がしないんだよ,hey help me doctor変なんです 味がしない死にそうです 9 funky flowで熱伝導 最新章のlet it flow 年中年中エンドレス シビレきらすファンクネス 1 世界一microphoneが 似合うプリンセスはだあれ i'm coming back yo you can't forget me yo 9 2、3日後体が空く 家まで迎えに行くからよまた会おう 降ろすrhyme災いや偽りはいらない 耳に入るその飛び偽物は効かない 5 いつでもどんなシチュエーションにも すぐに馴染んでいくでしょ?不思議 お前みたいなヤツは全然いない 変幻自在my baby 2 いらねえ警察 達麻の傑作 いくらつければ拭えるパクられる命落ちたか いつも居る崖の淵押すなバビロンこちら側 5 声パクリそしてフローパクリ ステージでの振る舞いも超パクリ マジ神経疑うぜまるでモノマネ歌合戦 親子で出にゃつまらんぜ 3
たくさん改良の余地があると思います。
- 量
- 曲の選定基準 (順位)
- 人によって量が違うので偏りが出そう
- バースの切り方
- 重複(サビなど) の扱い
- バトルなどでのパンチラインをデータに入れる
- etc...
日本語事前学習済みT5
こちらを参考にさせていただきました。
今回利用したcolab です。
FastAPI + Line Messenger API
こちらを参考にさせていただきました。
基本構成はこちらをベースにしています
Cloud Run
image をbuild する際、Cloud Build + GitHub でやろうとしたものの、学習済みモデルをそのままGitHub にpush できず、lfs もCloud Build でうまく動かずで苦戦しました。
結局今回はハードコードっぽく、CI でGCS から転送してbuild, image 化しています。
また、pytorch やモデルの読み込みでメモリを食うため、4GB インスタンスが必要です。
TF Lite 化などが気になります。
おわりに
BERT, T5 の流れのおかげで、エンコーダー・デコーダー形式のモデルの実装難易度がかなり下がったように感じました。
一番時間がかかったのは、データを収集する部分と、ブログの記事(文章) を書くところでした。。
良質なデータを集められるプラットフォームの必要性が増していることを感じます。
下記の記事にもある、データ・セントリックにも通ずるものがありそうです。
また、同記事にもあるように、エンコーダー・デコーダー形式の一般化が進んだことにより、画像や音声などの領域にも手が届きそうな状況になってきました。
テキストだけのラップボットは今年で卒業し、来年(度) こそはマルチモーダルな世界に足を踏み入れたいと思います。
ラップボット界のホームラン王、現る
こんにちは。ラップボットプロデューサーの @masaki925 です。
今年もこの季節がやってきました、毎年恒例のラップボットです。
昨年は流行りのBERT に手を出して痛い目を見ましたが、今年も懲りずにやっていきたいと思います。
ラップの強さと韻の飛距離
私の目標の1つは強いラップボットを作ることです。
では、強い、とはなんでしょうか?
韻の硬さ、フロウの柔軟さ、エモいバックグラウンド、色々あると思いますが、
ライムのクオリティを測る指標として、韻の飛距離という概念があります。
こちらの記事が参考になります。
https://news.1242.com/article/209776
R-指定:「A」という言葉と「B」という言葉で踏もうとしたら、「A」と「B」の言葉の響きは近ければ近いほどいい。でも、その内容がかけ離れていれば離れているほど、韻として面白いというか。
...
(中略)
...
R-指定:その時、ZORNさんがブワーッと電話越しで言ってくれたのが、「表参道のオープンカフェより、お嫁さんと食う醤油ラーメン」っていう。この飛距離、わかる?
バトル中にこんな飛距離あるライミングをされたら思わず吹っ飛ばされてしまいますね。
今年はこの韻の飛距離を計測すると同時に、宇宙の果てまで飛んでいくようなパンチラインをかますラップボットを開発しました。
韻の飛距離メー(タ|カ)ー MC ドカベン
韻の飛距離メーター 兼 韻の飛距離メーカー、MC ドカベンです。
あなたがキックしたバースの飛距離を採点するのと同時に、 アンサーとなるバースを飛距離と共に返し、飛距離を比較して勝敗を判定してくれます。
※ めちゃくちゃ重いです。。辛抱強くお試しください
国際航空連盟によれば高度100km (100,000m) からを宇宙と定義しているらしいので、宇宙旅行も夢じゃありませんね。
ぜひお友達になって、ガガーリンも真っ青な長距離砲ライムをかましてみてください。
※ 予算の都合上、年内いっぱいで活動休止するかもしれません。BERT メモリ食い過ぎ。。ということで、バトルはお早めに!
以降、開発の裏側の話です。
MC ドカベンのつくりかた
アルゴリズム編
まず、ライムを作るときの人間の思考方法について考えました。
それがこの図です。
時間がない 企業秘密のため詳細は省きますが、
要するに、「まず2小節目に使うキラーフレーズを選んで、そこに韻を乗せるように1小節目を構成する」です。
加えて、アンサー要素として「相手のバースに関連した内容を入れ込む」ことと、
レペゼン要素として「自分のキャラに合った内容を入れ込む」です。
MC ドカベンでは、この1小節目と2小節目の組み合わせをいくつか試行して、もっとも飛距離が出るものを採用することにしています。
※ 今回はとりあえずレペゼン「野球」に固定しています。
飛距離に関しては、ラップボットコンサルタントのkenichi 氏に教えてもらいました。まじリスペクト。
細かい部分は色々とありますが、ざっくり上記の方針でロジックを組んでいます。
実装編
ソースコードです。
前回までと同様、LINE Messenger API を利用して、フロントはRails, 裏側はFlask で構成しています。
学習済みモデルなどでかいファイルはignore してあります。
正直中身ぐちゃぐちゃです。
肝は、小節間の意味的な距離を測るためにBertScore を利用している部分でしょうか。
ただ、精度的にもサイズ的にもこれじゃなくてもいいのでは感が否めません。。今後の課題です。
キラーフレーズには、水島新司さんの野球漫画「ドカベン」から名言っぽいものを拝借しました。「飛距離」から着想を得ています。 今は数が少ないと、ラップっぽくないものも多数混ざってしまっているので、改善の余地が大量にあると思います。
文章生成のマルコフ連鎖の部分は、以前作ったDJマルコのコードをベースとして、少し改良したものになっています。
他にもBERT 使うためにDocker コンテナサイズと格闘した話(負けた) とか、Heroku でCedar が古すぎてupgrade のためにbuildpack と格闘したりしました(勝った) が、今年は 時間の都合 企業秘密で詳細は割愛します。
気が向いたら別記事を書くかも。
なにか気になるところあればコメントでもDM でもください。
以上です。良いお年を!
BERT でBATTLE
はじめに
こんにちは。
今日も元気に言葉のウェイトトレーニングしてますか?
本記事は、LINE Bot Advent Calendar 2019 の23日目の記事となります。
今回も懲りずに、ラップボットシリーズの第4作目です。
前回はマルコフ連鎖を用いて、血の気盛んなバトルラッパー DJマルコを生み出しました。
今回はもっとAI や機械学習的なことを ということで、 最近話題の自然言語処理モデル、BERT を利用してみました。
BERT に関しては、こちらの記事がわかりやすかったので、もしご存知ない方は参照してみてください。
Googleが公開した自然言語処理の最新技術、BERTとは何者なのか | AI-SCHOLAR
余談ですが、ラップボットが取り組む問題は文章生成なので、 個人的にはBERT と同じく話題の言語モデル GPT-2 (参考記事) を利用してみたかったのですが、 日本語での事前学習モデルが公開されていなかったため、 今回は京都大学の研究室が公開している、BERT の日本語事前学習モデルを利用させていただくこととしました。
BERT日本語Pretrainedモデル - KUROHASHI-KAWAHARA LAB
と、御託はこのくらいにして、今回のラッパーの紹介です。
今回のラッパーは、こいつだ!
本人は全くその気はないのに、大御所の存在により演歌歌手に間違えられがちな咆哮系ラッパー、鳥羽さんです。
ぜひお友達になって、バトルしてみてください!
… と言いたいところだったのですが、
なんとこの鳥羽さん、パフォーマンスに難ありでデプロイできず、
お披露目することができませんでした。。
くやしい。
代わりにいくつか生成したパンチラインを紹介するに留めておきたいと思います。
詳細は後述しますが、以下は収集したラップバトルのパンチラインを学習(fine tuning) させてモデルを構築し、文字数を指定(下記は40) して文章を自動生成したものになります。
おいACE[UNK][UNK][UNK][UNK][UNK][UNK]に教えてやる Beatで踏んでるお客さんそれがお豆
突然のお豆。 ([UNK] はUnkown の略で、うまく言葉を生成できなかった部分です。)
俺にとってはビビい所から来たメンタルはやっぱりこのスキルをアップデートしないとこだよ
くー。今回のスキル不足を指摘されてるみたいで刺さる。精進します。
アンタに勝てるぜレペゼンパッパラパパパパパッパラ そっからはお客さん
レペゼンどこそれ?からの無茶振りw
お前を見てヤバいブーメラン本当そうだな このステージのお母ちゃんテメーじゃん
「ブーメラン」「お母ちゃん」「テメーじゃん」で韻踏んでるような。。?
とまぁこんなかんじになりました。
正直、クオリティ的には過去のラッパーたちに比べても大きく改善したとはいいにくいですが、
「ビビい所」「パッパラパパパパパッパラ」といった造語っぽいものや、
上記にもあった韻を踏んでるっぽいところは、今後の可能性の片鱗を見せてくれたような気がします。
今後、言葉のウェイトとは裏腹に身軽になった鳥羽さんが降臨してくれることに期待しましょう…!!
以下、実装編となります。
鳥羽さんの作り方
概要
GitHub - masaki925/bert_rap: BATTLE on BERT
(あんまり整備できてないので気になる部分があればお気軽にお声がけください)
- 構成
方針
下記の記事を参考にさせていただきました。
こちらで紹介されている BertMouth は、 自分でテキストを用意してfine tuning することで独自モデルが構築できるようになっています。
生成時は指定された長さの文字列を穴埋め問題化して、1文字ずつ予測して埋めていくという手法です。
ので、今回のポイントは
- どういったデータを
- どのくらい
- どういった形式で用意するか
といったものでした。
まずはデータ集めです。
データセット
- フリースタイルダンジョン の書き起こしを利用させていただきました
放送回と、書き起こしサイトの対応表も一応載せておきます。
単純にクロールして済む問題でもないので、地道に丹精込めて集めました。
フリースタイルダンジョン書き起こし - Google スプレッドシート
※ なおYouTube の字幕も見てみましたが、やはり日本語の精度はまだ使い物にならなかったです。
単純な1行の文章でいくと、2722文を集めることができました。
ポケモンの方が4000文以上集めていたのでそこを目指したのですが、やや及ばずでした。
実物はこんなかんじです。
高鳴って来たぜ心拍数 コイツは俺に負けて自身無くす この会場で俺は進化する 俺がバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からすればボンクララッパー 返してこいちゃんとしたドープなアンサー
フリースタイルダンジョン:シーズン1 REC1:Dragon One VS T-PABLOW | フリースタイル文字起こし
これは記念すべき1回目の放送(っぽい) で、Dragon One のバースですね。
教科書のようなライミングです。
他にも、
Remember ファーストチャレンジャー I’m a danger あ?二代目だ?あんまナメんな これは事件だ 後ろにはアベンジャーズ やっぱり ヤベェメンバーも集まってんだよ 余裕で獲る天下 これは口喧嘩 度が過ぎてんだ 若干してるぜ 劣化 与えるぜ プレッシャー 速攻でステップアップ 崇勲? 俺1人で充分
https://hiphopzanmai.com/freestyledungeon/81/
またもやDragon One のバースですが、 こんなかんじで、短めの一行もあれば、これらがまとめて一行になっているものも混ざっています。
こちらを使って学習していきます。
学習
手元のMacbook で学習しようとしたところ時間がかかりすぎたため、colab を利用しました。
GPU がサクッと使えるのは感動的ですね。
デフォルトのランタイムがCPU にセットされていて、GPU に変更するとそれまで利用したファイルが消えてしまうので要注意です。
また時間(90分?) が経つとセッションが切られて、これまたダウンロード・インストールしたファイル (特にBERT モデルなど、大きいファイル) が削除されてしまいきっつー となるので、Auto Refresh を入れて定期的に接続するようにしました。
が、学習途中でリフレッシュされて学習が止まってしまったことがあった(たぶん。1回学習を回すのに1時間以上かかるため、待ちながら他のことやってたりするので記憶が曖昧になる。。) ので、要注意でした。。
学習の様子
12/21/2019 11:34:51 - INFO - __main__ - train starts Epoch: 0% 0/100 [00:00<?, ?it/s]12/21/2019 11:34:57 - INFO - __main__ - [1 epochs] train loss: 1.02 12/21/2019 11:34:58 - INFO - __main__ - sampled sequence: ののののののののののののののののののののののののは 12/21/2019 11:34:58 - INFO - __main__ - [1 epochs] valid loss: 1.02 Epoch: 1% 1/100 [00:06<11:14, 6.82s/it]12/21/2019 11:35:04 - INFO - __main__ - [2 epochs] train loss: 0.965 12/21/2019 11:35:05 - INFO - __main__ - sampled sequence: ははははははははははははははははははははははははは 12/21/2019 11:35:05 - INFO - __main__ - [2 epochs] valid loss: 0.96 Epoch: 2% 2/100 [00:13<11:16, 6.91s/it]12/21/2019 11:35:11 - INFO - __main__ - [3 epochs] train loss: 0.908 12/21/2019 11:35:12 - INFO - __main__ - sampled sequence: ののののののののののののののののののののののののは 12/21/2019 11:35:12 - INFO - __main__ - [3 epochs] valid loss: 0.894 Epoch: 3% 3/100 [00:20<11:06, 6.87s/it]12/21/2019 11:35:18 - INFO - __main__ - [4 epochs] train loss: 0.829 12/21/2019 11:35:21 - INFO - __main__ - sampled sequence: 俺はは俺は俺は俺は俺俺は俺俺は俺は俺は俺は俺は俺は 12/21/2019 11:35:21 - INFO - __main__ - [4 epochs] valid loss: 0.847 Epoch: 4% 4/100 [00:30<12:09, 7.60s/it]12/21/2019 11:35:27 - INFO - __main__ - [5 epochs] train loss: 0.799 12/21/2019 11:35:31 - INFO - __main__ - sampled sequence: 俺のの俺ののの俺ののがねぇねぇ俺のののののののねぇ 12/21/2019 11:35:31 - INFO - __main__ - [5 epochs] valid loss: 0.869 Epoch: 5% 5/100 [00:39<12:51, 8.12s/it]12/21/2019 11:35:36 - INFO - __main__ - [6 epochs] train loss: 0.771 12/21/2019 11:35:40 - INFO - __main__ - sampled sequence: ねねぇねねぇ俺はお前はお前はねぇ俺お前ははお前はお前はねねぇ 12/21/2019 11:35:40 - INFO - __main__ - [6 epochs] valid loss: 0.766 Epoch: 6% 6/100 [00:48<13:15, 8.47s/it]12/21/2019 11:35:46 - INFO - __main__ - [7 epochs] train loss: 0.762 12/21/2019 11:35:48 - INFO - __main__ - sampled sequence: がねねぇ俺は俺は俺は俺が俺がねぇぜ俺は俺は俺は俺は 12/21/2019 11:35:48 - INFO - __main__ - [7 epochs] valid loss: 0.82 Epoch: 7% 7/100 [00:56<13:04, 8.43s/it]12/21/2019 11:35:54 - INFO - __main__ - [8 epochs] train loss: 0.693 12/21/2019 11:35:55 - INFO - __main__ - sampled sequence: お前の俺の俺のの俺の俺の俺の俺の俺の俺ののだぜお前のの 12/21/2019 11:35:55 - INFO - __main__ - [8 epochs] valid loss: 0.827 Epoch: 8% 8/100 [01:04<12:18, 8.03s/it]12/21/2019 11:36:01 - INFO - __main__ - [9 epochs] train loss: 0.729 12/21/2019 11:36:04 - INFO - __main__ - sampled sequence: 俺はお前の俺だぜぜお前の俺のは俺の俺だぜ俺がだねぇねぇ 12/21/2019 11:36:04 - INFO - __main__ - [9 epochs] valid loss: 0.763 Epoch: 9% 9/100 [01:13<12:37, 8.32s/it]12/21/2019 11:36:10 - INFO - __main__ - [10 epochs] train loss: 0.738
最初は「俺の」しか言わない
12/21/2019 11:41:45 - INFO - __main__ - sampled sequence: 俺はオメェ俺はHIPHOPフロウだよ?まぁまぁラップしてるぜこの勝利 12/21/2019 11:41:45 - INFO - __main__ - [50 epochs] valid loss: 0.617 Epoch: 50% 50/100 [06:53<06:32, 7.84s/it]12/21/2019 11:41:50 - INFO - __main__ - [51 epochs] train loss: 0.528 12/21/2019 11:41:53 - INFO - __main__ - sampled sequence: 俺らはここで俺の方が俺はこの上でじゃないぜ俺の方が俺の方だ 12/21/2019 11:41:53 - INFO - __main__ - [51 epochs] valid loss: 0.607 Epoch: 51% 51/100 [07:01<06:27, 7.90s/it]12/21/2019 11:41:58 - INFO - __main__ - [52 epochs] train loss: 0.517 12/21/2019 11:42:01 - INFO - __main__ - sampled sequence: お前のフロウの上には踏めないオメェがヤバいしてるぜこのアンタの 12/21/2019 11:42:01 - INFO - __main__ - [52 epochs] valid loss: 0.561 Epoch: 52% 52/100 [07:10<06:29, 8.12s/it]12/21/2019 11:42:07 - INFO - __main__ - [53 epochs] train loss: 0.529 12/21/2019 11:42:11 - INFO - __main__ - sampled sequence: 俺の事俺は俺はこの事じゃないのは何だよ俺の事じゃないののか? 12/21/2019 11:42:11 - INFO - __main__ - [53 epochs] valid loss: 0.614 Epoch: 53% 53/100 [07:19<06:37, 8.46s/it]12/21/2019 11:42:16 - INFO - __main__ - [54 epochs] train loss: 0.478 12/21/2019 11:42:18 - INFO - __main__ - sampled sequence: 俺らはこの上のアンタに1人俺らはアンタはこの事じゃないぜ俺の事 12/21/2019 11:42:18 - INFO - __main__ - [54 epochs] valid loss: 0.611 Epoch: 54% 54/100 [07:27<06:18, 8.24s/it]12/21/2019 11:42:24 - INFO - __main__ - [55 epochs] train loss: 0.511 12/21/2019 11:42:28 - INFO - __main__ - sampled sequence: このアンタ俺がフリースタイル俺が俺の方がお前らだけじゃないオメェお前らだよ 12/21/2019 11:42:28 - INFO - __main__ - [55 epochs] valid loss: 0.627 Epoch: 55% 55/100 [07:36<06:24, 8.54s/it]12/21/2019 11:42:33 - INFO - __main__ - [56 epochs] train loss: 0.538 12/21/2019 11:42:36 - INFO - __main__ - sampled sequence: このクソだよアンタの上だけじゃねぇどっちがHIPHOPアンタだよここで 12/21/2019 11:42:36 - INFO - __main__ - [56 epochs] valid loss: 0.696 Epoch: 56% 56/100 [07:45<06:18, 8.59s/it]12/21/2019 11:42:42 - INFO - __main__ - [57 epochs] train loss: 0.531 12/21/2019 11:42:45 - INFO - __main__ - sampled sequence: フロウ俺はフロウで何年もここでここでここで踏めないか?ここでこの勝利 12/21/2019 11:42:45 - INFO - __main__ - [57 epochs] valid loss: 0.606 Epoch: 57% 57/100 [07:53<06:09, 8.60s/it]12/21/2019 11:42:51 - INFO - __main__ - [58 epochs] train loss: 0.518 12/21/2019 11:42:53 - INFO - __main__ - sampled sequence: ヤバい言葉の上俺がヤバいぜ俺らがこのフロウの上じゃない俺らが 12/21/2019 11:42:53 - INFO - __main__ - [58 epochs] valid loss: 0.568 Epoch: 58% 58/100 [08:02<05:58, 8.54s/it]12/21/2019 11:42:59 - INFO - __main__ - [59 epochs] train loss: 0.536 12/21/2019 11:43:03 - INFO - __main__ - sampled sequence: 何がないぜこの事フリースタイルマジでフロウだけじゃないぜオメェはヤバいだけだ 12/21/2019 11:43:03 - INFO - __main__ - [59 epochs] valid loss: 0.63 Epoch: 59% 59/100 [08:11<05:58, 8.75s/it]12/21/2019 11:43:08 - INFO - __main__ - [60 epochs] train loss: 0.503
だいぶ語彙が増えてくるが、同じ言葉を繰り返してしまったりしている。
12/21/2019 11:47:37 - INFO - __main__ - sampled sequence: FORK俺が俺の方俺の方がこのラップには俺の方じゃないぜどっちの方 12/21/2019 11:47:37 - INFO - __main__ - [91 epochs] valid loss: 0.578 Epoch: 91% 91/100 [12:45<01:17, 8.63s/it]12/21/2019 11:47:42 - INFO - __main__ - [92 epochs] train loss: 0.455 12/21/2019 11:47:45 - INFO - __main__ - sampled sequence: このラップは踏めないぜお前らは俺のオメェのフロウはHIPHOP俺の方 12/21/2019 11:47:45 - INFO - __main__ - [92 epochs] valid loss: 0.585 Epoch: 92% 92/100 [12:54<01:08, 8.62s/it]12/21/2019 11:47:51 - INFO - __main__ - [93 epochs] train loss: 0.428 12/21/2019 11:47:54 - INFO - __main__ - sampled sequence: このフロウフロウ大丈夫だろ大丈夫俺はアンタにはここにはない大丈夫俺は 12/21/2019 11:47:54 - INFO - __main__ - [93 epochs] valid loss: 0.605 Epoch: 93% 93/100 [13:02<00:59, 8.55s/it]12/21/2019 11:47:59 - INFO - __main__ - [94 epochs] train loss: 0.418 12/21/2019 11:48:01 - INFO - __main__ - sampled sequence: 俺の方がこの勝利まぁ[UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK][UNK] 12/21/2019 11:48:01 - INFO - __main__ - [94 epochs] valid loss: 0.624 Epoch: 94% 94/100 [13:09<00:49, 8.19s/it]12/21/2019 11:48:07 - INFO - __main__ - [95 epochs] train loss: 0.423 12/21/2019 11:48:09 - INFO - __main__ - sampled sequence: 俺の方ぶちがコイツがどっちとかだよ?お前らのフロウで俺らは 12/21/2019 11:48:09 - INFO - __main__ - [95 epochs] valid loss: 0.553 Epoch: 95% 95/100 [13:17<00:40, 8.13s/it]12/21/2019 11:48:15 - INFO - __main__ - [96 epochs] train loss: 0.435 12/21/2019 11:48:18 - INFO - __main__ - sampled sequence: 来い大丈夫だ俺は俺らだったらNo.1俺ら1人の方が俺の方だ 12/21/2019 11:48:18 - INFO - __main__ - [96 epochs] valid loss: 0.565 Epoch: 96% 96/100 [13:26<00:32, 8.20s/it]12/21/2019 11:48:23 - INFO - __main__ - [97 epochs] train loss: 0.419 12/21/2019 11:48:26 - INFO - __main__ - sampled sequence: 何?俺が俺の方が俺がこのラップだけだよ?お前らは大丈夫?ここで俺 12/21/2019 11:48:26 - INFO - __main__ - [97 epochs] valid loss: 0.582 Epoch: 97% 97/100 [13:34<00:24, 8.26s/it]12/21/2019 11:48:32 - INFO - __main__ - [98 epochs] train loss: 0.464 12/21/2019 11:48:35 - INFO - __main__ - sampled sequence: この勝利俺はNo.大丈夫だろこの場でラップO....ラップしてるぜ一回 12/21/2019 11:48:35 - INFO - __main__ - [98 epochs] valid loss: 0.634 Epoch: 98% 98/100 [13:43<00:17, 8.55s/it]12/21/2019 11:48:41 - INFO - __main__ - [99 epochs] train loss: 0.431 12/21/2019 11:48:43 - INFO - __main__ - sampled sequence: もうここで1回もコイツ俺はこの上に行くぜこのラップよりもいいよそんな事だぜ 12/21/2019 11:48:43 - INFO - __main__ - [99 epochs] valid loss: 0.647 Epoch: 99% 99/100 [13:51<00:08, 8.30s/it]12/21/2019 11:48:48 - INFO - __main__ - [100 epochs] train loss: 0.418 12/21/2019 11:48:51 - INFO - __main__ - sampled sequence: くだらうぜフロウの事俺らの方じゃないのかだろここで俺らの方が 12/21/2019 11:48:51 - INFO - __main__ - [100 epochs] valid loss: 0.523
あんまりいい例を選び損ねてしまったのですが、段々と性能が上がっていることが見て取れます。
試したこと
学習で試したパターンは以下の通りです
- 1行ずつ
- 1-1. 約1200文 (train : valid = 600 : 100)
- 1-2. 約2700文 (2500 : 200)
- 1ターンをまるごと1行にする
- 2-1. 約380文 (300 : 80)
- 2-2. 約2400文 水増し + (2200 : 200)
- 2-3. 約2400文 水増し + 半角スペースを全角スペースに置換 したもの (2200 : 200)
まず、最初の1200文ではいい文章にならなかったため、諦めて 気合を入れて残りを集めました。データ量は正義ですね。
まるごと1行に関しては、「アンサー」を再現したかったので実施しました。
XXXXX >>> OOOOO
のような形式に加工し、右側(O の部分) を予測するよう学習させれば、ラップバトルのように相手の言葉を加味して返答を考える、といったことを期待しました。
また、1つ前だけではなく複数前のターンの内容も加味させられると面白くなるかなぁと。
その前段として、1行にまとめるのを試して、、、今回は燃え尽きました。
また「水増し」に関して、
1行にまとめると文章数が減ってしまうため、やはり精度が悪く使い物になりませんでした。
そこで、汚職政治家と機械学習業界ではおなじみの水増しを実施しました。
文章数を6倍くらいにしたかったため、 文章の中から名詞を抽出し、random で6つ選び、韻を踏んでいる単語に置き換えました。単語の検索には前回もお世話になった、「WEB 便利ツール」を利用しました。
こんなかんじ
変換前: ドープ 変換後: 覆う 高鳴って来たぜ心拍数 コイツは俺に負けて自身無くす この会場で俺は進化する 俺がバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からすれば ボンクララッパー 返してこいちゃんとした覆うなアンサー 変換前: 会場 変換後: 靉嘔 高鳴って来たぜ心拍数 コイツは俺に負けて自身無くす この靉嘔で俺は進化する 俺がバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からすれば ボンクララッパー 返してこいちゃんとしたドープなアンサー 変換前: 俺 変換後: カラオケ 高鳴って来たぜ心拍数 コイツはカラオケに負けて自身無くす この会場でカラオケは進化する カラオケがバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター カラオケからすれば ボンクララッパー 返してこいちゃんとしたドープなアンサー 変換前: 心拍 変換後: 金髪 高鳴って来たぜ金髪数 コイツは俺に負けて自身無くす この会場で俺は進化する 俺がバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からすれば ボンクララッパー 返してこいちゃんとしたドープなアンサー 変換前: 自身 変換後: 政審 高鳴って来たぜ心拍数 コイツは俺に負けて政審無くす この会場で俺は進化する 俺がバトルを始めればみんな夢中 ゴングは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からすれば ボンクララッパー 返してこいちゃんとしたドープなアンサー 変換前: ゴング 変換後: レスポンス 高鳴って来たぜ心拍数 コイツは俺に負けて自身無くす この会場で俺は進化する 俺がバトルを始めればみんな夢中 レスポンスは鳴ったぜ文句はあっか これは荒手のモンスターハンター 俺からす れば ボンクララッパー 返してこいちゃんとしたドープなアンサー
さらに、文章の途中に切れ目を入れてくれないことが気に食わなかったため、半角スペースを全角スペースに直したパターンも試しました。
ただ、思い通りにいかなかったため、冒頭の生成文では時間が無いので手元にあった それなりに精度の出た2-2. のモデルを採用しました。
トラブルシューティング
メモ程度。。
Mac にJuman++ 入れるの大変
- => Docker 使え
ValueError: invalid literal for int() with base 10: '\'
- => Juman は半角使えない
http://nlp.ist.i.kyoto-u.ac.jp/index.php?KNP%2FFAQ
半角スペースを含む文が解析できないのですが。 † JUMAN/KNPでは、入力文が全角文字で記述されていることを前提としています。半角スペースなどの半角文字は全角文字に変換してから入力してください。
ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11' not found (required by /opt/conda/lib/python3.6/site-packages/torch/lib/libtorch_python.so)
- よくわからないが、リンカの設定が食い違っている? Tokenizer の前にimport torch しないといけない。謎。
Cloud Run
- Memory limit of 256M exceeded with 276M used. Consider increasing the memory limit, see https://cloud.google.com/run/docs/configuring/memory-limits
files named ['vocab.txt'] but couldn't find such vocabulary files at this path or url.
- gcloud build submit は、.gitignore を踏襲するので、model ディレクトリがpush されてなかった
OSError: Model name './rap_bert_model' was not found in tokenizers model name list (bert-base-uncased, ..., bert-base-german-dbmdz-uncased). We assumed './rap_bert_model' was a path or url to a directory containing vocabulary files named ['vocab.txt'] but couldn't find such vocabulary files at this path or url.
- すぐDisk Full になる
- Docker で試行錯誤してるとすぐ一杯になる
- いま250GB しか積んでないから、次買い換えるときは拡張しよう。。
今後の展望
今回はBERT の導入と学習に精一杯で、ユーザー目線での改善がほとんどできませんでした。
これではユーザー会のみなさんに怒られてしまうので、下記を意識して、次回作では技術に走りすぎないように留意したいと思います。
ユーザー観点でのラップボットとしての改善点
- 成長
- 音楽
- DOPE 度測りたい
- 1つ前までしか(今までの内容を) 覚えてくれない
文章生成モデルとしての改善点
以上です、長文お読みいただきありがとうございました。
Flutter でScrollView がrebuild されたときにscroll to top したい
TL;DR
scheduler 使うとよい。(マサカリ歓迎)
import 'package:flutter/scheduler.dart'; class _MyWidgetState extends State<MyWidget> { ScrollController _controller; @override void initState() { super.initState(); _controller = ScrollController(); } @override Widget build(BuildContext context) { if (_controller.hasClients) { SchedulerBinding.instance.addPostFrameCallback((_) => _controller.jumpTo(0.0)); } return Container( child: ...
エラーログ
そのままやると:
Widget build(BuildContext context) { if (_controller.hasClients) { _controller.jumpTo(0.0); }
怒られる
Performing hot reload... Syncing files to device iPhone XR... Reloaded 1 of 553 libraries in 949ms. flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ flutter: The following assertion was thrown building MyWidget(dirty, dependencies: flutter: [_LocalizationsScope-[GlobalKey#ab210], _InheritedTheme], state: _MyWidgetState#100b7): flutter: Unexpected call to replaceSemanticsActions() method of RawGestureDetectorState. flutter: The replaceSemanticsActions() method can only be called outside of the build phase.
参考
-
- 今回はjumpTo を使ったけどanimateTo などもある
-
- (re) build の後に必ず呼ばれるメソッドが見つけられなかった。というかどこかにオフィシャルドキュメントありそう
-
- 自分はエラーメッセージから類推して「flutter how to add task after build phase has finished」でググって行き着いた
さて、なにしようとしてたんだっけ。そうだ、朝メシ。
JS周りのリハビリ備忘録(React + Babel + Webpack4)
今さらながらReact 入門しようと思ったんですが、JS 界隈の時代の流れに完全に置いてけぼりになっていました。
https://html5hive.org/react-tutorial/
↑ これをやってるときに、「あれ、browser-sync で表示されるのはいいとして、普通にfile open で開けないんだっけ?」と思っていろいろ調べたのがきっかけです。
※ ちなみに、browser-sync
$ browser-sync start --server . --files "**/*"
単純にfile open すると、ブラウザのconsole に下記エラーが表示されていました。
Access to XMLHttpRequest at 'file://~/tmp/my_react_tutorial2/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https. transform.load @ browser.min.js:4
次に、
<script type="text/babel" src="main.js"></script>
の type="text/babel"
を消してみたところ、
Uncaught SyntaxError: Unexpected token <
というエラーが出ました。以下、リハビリのためにこのあたりを解消していった備忘録です。
サマリ
環境
$ sw_vers ProductName: Mac OS X ProductVersion: 10.13.6 BuildVersion: 17G4015 $ npm -v 5.6.0 $ npx -v 9.7.1
before (ファイル構成)
$ tree . . ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js 3 directories, 4 files
作業
$ npm i -D babel-core babel-loader@7 babel-preset-react webpack webpack-cli
$ npm list --depth=0
webpack-demo@1.0.0 ~/tmp/my_react_tutorial2
├── babel-core@6.26.3
├── babel-loader@7.1.5
├── babel-preset-react@6.24.1
├── lodash@4.17.11
├── webpack@4.28.2
└── webpack-cli@3.1.2
$ cat webpack.config.js module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ], } ] } };
$ cat package.json { "name": "webpack-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-react": "^6.24.1", "webpack": "^4.28.2", "webpack-cli": "^3.1.2" } }
$ mkdir src $ mv main.js src/index.js
$ tree . -L 1 . ├── babel.config.js ├── dist ├── node_modules ├── package-lock.json ├── package.json ├── src └── webpack.config.js 3 directories, 4 files $ tree dist/ src dist/ ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js src └── index.js 3 directories, 5 files $ tree dist/ src/ dist/ ├── assets │ ├── css │ │ └── base.css │ └── images │ └── home.jpg ├── index.html └── main.js src/ └── index.js 3 directories, 5 files
webpack した結果をfile open
$ npx webpack
$ mv index.html assets/ dist/
$ open dist/index.html
これで、browser-sync したときと同じように表示されました。
以下、調査メモ
まず、手動でbabel でtranspile するとどうなるのか。
$ npm install --save-dev @babel/core @babel/cli @babel/preset-env $ npm install --save @babel/polyfill $ cat babel.config.js const presets = [ [ "@babel/env", { targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1", }, useBuiltIns: "usage", }, ], ]; module.exports = { presets };
$ ./node_modules/.bin/babel src --out-dir lib { SyntaxError: ~/tmp/my_react_tutorial2/src/main.js: Unexpected token (114:8) 112 | //as a prop, so we need a separate id for that. 113 | return ( > 114 | <Home | ^ 115 | key={index} 116 | id={index} 117 | onToggleSave={toggleSave} at Parser.raise (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:4051:15) at Parser.unexpected (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5382:16) at Parser.parseExprAtom (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6541:20) at Parser.parseExprSubscripts (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6104:21) at Parser.parseMaybeUnary (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6083:21) at Parser.parseExprOps (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5968:21) at Parser.parseMaybeConditional (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5940:21) at Parser.parseMaybeAssign (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:5887:21) at Parser.parseParenAndDistinguishExpression (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6699:28) at Parser.parseExprAtom (~/tmp/my_react_tutorial2/node_modules/@babel/parser/lib/index.js:6473:21) pos: 3211, loc: Position { line: 114, column: 8 }, code: 'BABEL_PARSE_ERROR' }
JSX がパースできていないらしい。
React はJSX と使うのがいいよ って話でBabel が入っていると認識していたのに、Babel だけではJSX はパースできないとは何事だ。
を見ていると、webpack の話が出てくる。
そもそもBabel って何者だっけ、とかwebpack とはどういう関係だっけ、とか思い始めて調べ始める。
↑ このあたりはとても参考になりました。
とりあえず概念を掴んだので、webpack からbabel を呼ぼうとする
$ npm run webpack npm ERR! missing script: webpack npm ERR! A complete log of this run can be found in: npm ERR! ~/.npm/_logs/2018-12-26T07_26_02_958Z-debug.log
ちょっとよくわからない。あ、local にインストールしたからか、と思って --development をつけても一緒。
ここらへんをみると、どうやら最近は npx
を使うようになっているらしいのでやってみる
$ npx webpack Insufficient number of arguments or no entry found. Alternatively, run 'webpack(-cli) --help' for usage info. Hash: b98875b6bc270ed9ed54 Version: webpack 4.28.2 Time: 64ms Built at: 2018/12/26 16:30:09 WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in Entry module not found: Error: Can't resolve './src' in '~/tmp/my_react_tutorial2'
src ディレクトリは存在しているのに。。
を見ると、設定ファイルが無いとデフォルトで src/index.js を見に行く、とあるので、rename してみる
$ mv src/main.js src/index.js $ npx webpack Hash: 2887e62448d27ce303a0 Version: webpack 4.28.2 Time: 285ms Built at: 2018/12/26 16:34:37 1 asset Entrypoint main = main.js [0] ./src/index.js 268 bytes {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js 114:8 Module parse failed: Unexpected token (114:8) You may need an appropriate loader to handle this file type. | //as a prop, so we need a separate id for that. | return ( > <Home | key={index} | id={index}
元のエラーに戻った。
これでwebpack が使えるようになったっぽいので、元のStackoverflow に戻って、 見よう見まねでwebpack のconfig を書いて実行してみる
$ cat webpack.config.js { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ], } $ npx webpack Unexpected token :
シンタックスエラー。リハビリ中なのでしょうがない。正しくは、上記を rules
に入れなきゃいけなかった (Stackoverflow にも書いてあった)
module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: ['react'] } } ] } ] } };
$ npx webpack Insufficient number of arguments or no entry found. Alternatively, run 'webpack(-cli) --help' for usage info. Hash: 53a7a87a9524e153353b Version: webpack 4.28.2 Time: 49ms Built at: 2018/12/26 16:41:37 WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in Entry module not found: Error: Can't resolve 'babel-loader' in '~/tmp/my_react_tutorial2'
足りないモジュールを入れてリトライ
$ npm install babel-core babel-loader --save-dev npm WARN babel-loader@8.0.4 requires a peer of @babel/core@^7.0.0 but none is installed. You must install peer dependencies yourself. npm WARN babel-loader@8.0.4 requires a peer of webpack@>=2 but none is installed. You must install peer dependencies yourself. npm WARN my-awesome-package@1.0.0 No description npm WARN my-awesome-package@1.0.0 No repository field. npm WARN my-awesome-package@1.0.0 No license field. + babel-loader@8.0.4 + babel-core@6.26.3 added 48 packages, removed 520 packages and moved 5 packages in 8.407s $ npx webpack One CLI for webpack must be installed. These are recommended choices, delivered as separate packages: - webpack-cli (https://github.com/webpack/webpack-cli) The original webpack full-featured CLI. We will use "npm" to install the CLI via "npm install -D". Do you want to install 'webpack-cli' (yes/no): yes Installing 'webpack-cli' (running 'npm install -D webpack-cli')... npm WARN babel-loader@8.0.4 requires a peer of @babel/core@^7.0.0 but none is installed. You must install peer dependencies yourself. npm WARN babel-loader@8.0.4 requires a peer of webpack@>=2 but none is installed. You must install peer dependencies yourself. npm WARN webpack-cli@3.1.2 requires a peer of webpack@^4.x.x but none is installed. You must install peer dependencies yourself. npm WARN my-awesome-package@1.0.0 No description npm WARN my-awesome-package@1.0.0 No repository field. npm WARN my-awesome-package@1.0.0 No license field. + webpack-cli@3.1.2 added 76 packages in 2.462s { Error: Cannot find module 'webpack-cli' at Function.Module._resolveFilename (module.js:555:15) at Function.Module._load (module.js:482:25) at Module.require (module.js:604:17) at require (internal/module.js:11:18) at runCommand.then (~/.npm/_npx/45193/lib/node_modules/webpack/bin/webpack.js:142:5) at <anonymous> at process._tickCallback (internal/process/next_tick.js:160:7) code: 'MODULE_NOT_FOUND' } $ npm list | grep webpack-cli └─┬ webpack-cli@3.1.2 npm ERR! peer dep missing: @babel/core@^7.0.0, required by babel-loader@8.0.4 npm ERR! peer dep missing: webpack@>=2, required by babel-loader@8.0.4 npm ERR! peer dep missing: webpack@^4.x.x, required by webpack-cli@3.1.2
webpack-cli は入ってるはずなのに、存在しないからインストールしようとしてくる。yes と答えてインストールしても、また同じエラーになる。
どういうことなの。。。
$ which npx /usr/local/bin/npx $ ll /usr/local/bin/npx lrwxr-xr-x 1 admin 46 12 17 2017 /usr/local/bin/npx@ -> /usr/local/lib/node_modules/npm/bin/npx-cli.js $ ll /usr/local/lib/node_modules/ | grep webpack $
どうやらnpm のlocal にうまくwebpack が入っていなかったようで、もう一度npm i -D webpack
する
$ ll node_modules/ | grep webpack drwxr-xr-x 7 staff 224 12 26 16:48 webpack-cli/ #=> webpackが入ってない!
$ npx webpack Hash: c0af3a1e4f45824bccdb Version: webpack 4.28.2 Time: 263ms Built at: 2018/12/26 16:55:19 1 asset Entrypoint main = main.js [0] ./src/index.js 2.81 KiB {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js Module build failed (from ./node_modules/babel-loader/lib/index.js): Error: Cannot find module '@babel/core' babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'. at Function.Module._resolveFilename (module.js:555:15) at Function.Module._load (module.js:482:25) at Module.require (module.js:604:17) at require (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:159:20) at Object.<anonymous> (~/tmp/my_react_tutorial2/node_modules/babel-loader/lib/index.js:10:11) at Module._compile (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:178:30) at Object.Module._extensions..js (module.js:671:10) at Module.load (module.js:573:32) at tryModuleLoad (module.js:513:12) at Function.Module._load (module.js:505:3) at Module.require (module.js:604:17) at require (~/tmp/my_react_tutorial2/node_modules/v8-compile-cache/v8-compile-cache.js:159:20) at loadLoader (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/loadLoader.js:13:17) at iteratePitchingLoaders (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/LoaderRunner.js:169:2) at runLoaders (~/tmp/my_react_tutorial2/node_modules/loader-runner/lib/LoaderRunner.js:362:2) at NormalModule.doBuild (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModule.js:280:3) at NormalModule.build (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModule.js:427:15) at Compilation.buildModule (~/tmp/my_react_tutorial2/node_modules/webpack/lib/Compilation.js:633:10) at moduleFactory.create (~/tmp/my_react_tutorial2/node_modules/webpack/lib/Compilation.js:1019:12) at factory (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:405:6) at hooks.afterResolve.callAsync (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:155:13) at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (~/tmp/my_react_tutorial2/node_modules/tapable/lib/HookCodeFactory.js:32:10), <anonymous>:6:1) at AsyncSeriesWaterfallHook.lazyCompileHook (~/tmp/my_react_tutorial2/node_modules/tapable/lib/Hook.js:154:20) at resolver (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:138:29) at process.nextTick (~/tmp/my_react_tutorial2/node_modules/webpack/lib/NormalModuleFactory.js:342:9) at process._tickCallback (internal/process/next_tick.js:150:11)
バージョン依存があったらしい。勝手に解決してくれないのか。自分で指定して入れる。
npm i -D babel-loader@7
$ npx webpack Hash: 75e3609ad92c952b5d1e Version: webpack 4.28.2 Time: 636ms Built at: 2018/12/26 16:59:07 1 asset Entrypoint main = main.js [0] ./src/index.js 1.59 KiB {0} [built] [failed] [1 error] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/ ERROR in ./src/index.js Module build failed (from ./node_modules/babel-loader/lib/index.js): Error: Couldn't find preset "react" relative to directory "~/tmp/my_react_tutorial/src" at ~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:293:19 at Array.map (<anonymous>) at OptionManager.resolvePresets (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:275:20) at OptionManager.mergePresets (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:264:10) at OptionManager.mergeOptions (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:249:14) at OptionManager.init (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12) at File.initOptions (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/index.js:212:65) at new File (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/file/index.js:135:24) at Pipeline.transform (~/tmp/my_react_tutorial/node_modules/babel-core/lib/transformation/pipeline.js:46:16) at transpile (~/tmp/my_react_tutorial/node_modules/babel-loader/lib/index.js:50:20) at Object.module.exports (~/tmp/my_react_tutorial/node_modules/babel-loader/lib/index.js:173:20)
今度はreact が見つからない、とのことなので、関係ありそうなモジュールを追加
$ npm i -D babel-preset-react
$ npx webpack Hash: 3500f1c5383324b5f4a0 Version: webpack 4.28.2 Time: 412ms Built at: 2018/12/26 17:00:28 Asset Size Chunks Chunk Names main.js 3.22 KiB 0 [emitted] main Entrypoint main = main.js [0] ./src/index.js 3.84 KiB {0} [built] WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
これで、ようやく dist 配下に、bundle されたmain.js が置かれた。
時代の流れについていくのは大変だなぁ。
あれ、何しようとしてたんだっけ
AWS Lambda でHeroku の無料枠でサーバーが寝ないようにする(2018年版)
先日、ラップボットのDJ マルコをご紹介しました。
DJ マルコはheroku にデプロイしてるんですが、下記のような問題がありました。
- フロント(Rails on Heroku) とバック(Flask on Heroku) の2台構成になっている
- heroku サーバーは30分リクエストが無いと寝てしまうので、ただでさえ起動が遅いのに加えて、フロントがバックの起動を待っている間にタイムアウトして、必ず1リクエスト目がエラーになってしまっていた
そのため、定期的にping して起こしておきたいと思いました。 少し調べたところ、下記の記事が見つかりました。
UptimeRobot は設定が非常に簡単で良かったのですが、監視の時間帯を設定することができないようでした。
そうすると、24時間 2つのdyno が動き続けることになり、合計で24 * 2 * 30 = 1440 時間となってしまい、無料枠の1000時間を超えてしまう問題がありました。
他の案も、dyno 時間を消費してしまうので、今回の要件には合わなそうでした。
そこで、AWS Lambda でhealth check だけするfunction を作って、AWS Watch Rules で昼間だけ動くようにしました
- lambda_handler.py
from urllib.request import urlopen from datetime import datetime, timedelta def validate(expected, res): return expected in str(res) def lambda_handler(event, context): try: if not validate(event['expected'], urlopen(event['site']).read()): raise Exception('Validation failed') except: print('Check failed!') raise else: print('Check passed!') return 'OK' finally: print('Check complete at {}'.format(str(datetime.utcnow() + timedelta(hours=9))))
ping 対象のURL と、チェック文言を起動時の引数で与えるようにしているので、他のURL でも流用が可能です。
今回は
{"site": "https://rhyme-bot.herokuapp.com/health/marco", "expected": "OK"}
これで、9時〜24時の15時間だけ起こしておくことになったので、15 * 2 * 30 = 900 時間で、無料枠(1000時間) に無事収まります。やったね!
マルコフ連鎖でラップボットつくった
追記 (2020/01/14)
BERT 版はこちら
はじめに
こんにちは。ヴァイブス満タンですか?
この投稿は LINEBot&Clova Advent Calendar 2018 の14日目の投稿です。
昨年はDopeLearning を使って、ノスタルジックなラッパー、ピーさんをリリースしました。
昨年を振り返って反省したこととしては、「バトル感が無い」ということでした。
そこで今年はもっとバトル感を醸成すべく、世のラッパーたちが過去に残したパンチラインを学習し、前人未踏のパンチの効いたヴァースをかますラッパーを生み出したいと考えました。
そこで目をつけたのが、「マルコフ連鎖」という文章生成モデルです。
マルコフ連鎖での文章生成については、下記の記事がわかりやすかったので紹介させていただきます。
引用元: マルコフ連鎖による文章生成 - 知識のサラダボウル
簡単に言うと、上記の図の通り、「ある単語が来たら次に来そうな単語はこれだ」というパターンをあらかじめ学習しておくことで、パターンに沿って自由に文章を生み出すことができるようになります。
そうして生まれたのがこちら、「お麩」界のカリスマこと、DJ マルコ です。
マルコにバトルを仕掛けると、こんなかんじでケンカを買ってくれます。
いかがでしょうか。
「お前のライム ナチュラル無農薬野菜」は、攻撃力が無くてむしろ健康になっちゃうぜ、的なdis りなのか、あるいは手間ひまかけていいもの作ってるね、的な賛辞なのか、一瞬戸惑ってしまう奥深いパンチラインですね。
そんなDJ マルコとバトルをしてみたいという奇特な方がいらっしゃったら、ぜひ下記のリンクからバトルを仕掛けてみてください。
DJ マルコの育て方
サーバー構成について
ラップ生成エンジンについて
- パンチラインDB として、韻ノート の、「最近追加された韻」欄を参照させていただきました
- パンチラインDB から文章を生成するため、TextGenerator を一部改変して利用しました
大変だったこと
どうすればラップっぽい文章が生成できるか
マルコフ連鎖で今回ポイントになると考えた点と、TextGenerator での実装は下記の通りでした。
- スタートをどう始めるか
- 開始フラグ(
__BEGIN__
) がついたノードをランダムに選択
- 開始フラグ(
- 途中のノードをどう選択するか
- 出現比率に応じてランダム
- 文章の最後はどう終わるか
- 終了フラグ(
__END__
) がついたノードに当たったら
- 終了フラグ(
ここで、ラップバトルには「アンサー」と呼ばれる、相手のヴァースに対してメンションし、うまく次の攻撃に繋げるという特徴があることから、スタート部分を下記のとおり改変しました。
しかし、今回は、パンチラインの数が少なかったこともあり、該当する単語がDB に無いことが頻発しました。
そこでword2vec で類似する単語を推定し、元の単語が見つからないときには類似単語でレコードから参照するようにしています。
word2vec の語彙コーパスとしては、 Livedoor コーパス を利用しました。
その他つまづきポイント
heroku container:push が重い
MeCab の辞書を含んでいたため、1GB を超えるファイル転送が走り、待ち時間が長くなってしまうことがあったため、なるべくローカルでdocker run して問題がないことを確認してからpush するようにしました
Mac とalpine のmecab で、surface の返り値が異なる
これがなかなか曲者で、「俺がウドの大木」という文章をパースした際、下記のような違いがありました
mt = MeCab.Tagger() node = mt.parseToNode('俺がウドの大木') node = node.next # EOS をスキップ node.feature #=> '名詞,代名詞,一般,*,*,*,俺,オレ,オレ' node.surface #=> '俺'
Alpine
(途中省略) node.feature #=> '名詞,代名詞,一般,*,*,*,俺,オレ,オレ' node.surface #=> '俺がウドの大木'
わかりづらい。。
今後の展望
以上となります。