しろあんのさかな

ふーちゃのエンジニアブログ

修士2年で研究室を辞めて新しい研究室で2ヶ月で論文投稿した話

修士2年の4月に研究室を辞めて新しい研究室に入り、2ヶ月で査読なしの国内学会に6ページの論文を投稿した話です。タイトルはちょっと強烈かもしれませんが、この記事は前の研究室の悪口を書きたいわけではなく、研究室を移動したことでうまく気持ちが切り替わって2ヶ月間必死に取り組んで論文投稿までできた話を書いています。

研究室移動の経緯

今年の4月に学部4年生から2年間お世話になった研究室を辞めました。研究室の雰囲気、研究の題材は結構気に入っていたので2年間それなりに楽しく、辛いことがあっても周りと励ましあって続けてきました。しかしどうしても指導のスピード、ゼミの雰囲気が自分に合わなかったこと、そして自分がこの2年間のうちにそれをどうにか改善することができなかったことが理由で、修士1年の後半あたりから来年1年はどうしようかと考え始めました。そして今年の3月にあと1年今の気持ちでここにいると惰性で過ごして適当に修士号をとってつまらない気持ちで卒業をするんだろうなと思い、今の指導教員と前の指導教員の両方の承諾を得て研究室を移動させてもらいました。

運が良くて2ヶ月で移動~論文投稿までいけた

私が研究室を移動してだいたい4月の中旬からスムーズに研究をはじめられたのは、いろいろな面で良い環境があったからだと思います。

  • 学部時代からお世話になっていた今の指導教員

    • 学部2年生の特別演習の授業で1年間指導教員をしてくださっていた先生でした。
    • 授業の印象を覚えていただいており、私にあった進めやすさを一緒に考えてくださいました。
  • Verilog-HDLを利用した2年間の開発経験があった。

    • 学部2年
      • FPGA(Field Programable Gate Array)で並列処理による高速化を生かしたリアルタイムフィルタ
      • CORDICアルゴリズムとPWM(Pulse Width Modulation)制御で生成したsin波を利用した自動演奏回路
    • 学部3年
      • FPGA・ディスプレイ間のデータ送信回路
      • カメラ・FPGA間のデータ受信回路
      • SDRAMの読み出し・書き込みのためのAXIバスの記述
  • 両指導教員からのサポート

    • 前の研究と今の研究で少しテーマを変えなければいけなかったので、上手く前の研究を引き継げるように考えてくださいました。
    • 今回2ヶ月で論文投稿できたのも今の指導教員だけでなく前の指導教員のおかげでもあると思っています。
  • 両研究室の学生からのサポート

    • 移る上で困ること、私がいなくなった後の係の仕事や新しい研究室でのデスク環境の整備、メールアドレスの管理など大変なことも忙しい中で大丈夫だよと引き受けていただきました。
    • 前の研究室の方にも今の研究室の方にも仲良くしていただき、本当に成果が出るのかとか、特に移ったばかりは不安な気持ちがたくさんあったのでありがたかったです。

論文投稿するまでのスケジュール

今の研究室で、私の指導教員のチームはFPGAを用いた研究をするチームです。論文を書くためにはFPGA上での実験をしなければならないので、最終的にはFPGAに自作回路をのせて自分の思い通りの動作をさせなければなりません。自作回路はVerilog-HDLというハードウェア記述言語で記述できるのですが、開発の流れとしては、自作回路の記述・記述の動作確認シミュレーション・記述からbitファイルの生成(コンパイルみたいな感じでFPGAに理解できる形式に変換されます)・本番ボードで動作確認をしていきます。自作回路記述、動作シミュレーションまではボードに依存せずに行えるのですが、bitファイル生成と本番ボードの動作確認はボードに依存し、シミュレーション通りに動かないこともあります。私は4月中旬からGW明けまでに動作シミュレーションまで終わらせて、GW明けから6月頭までにbitファイル生成と本番ボードでの動作確認を終わらせました。6月頭から6月中旬までに自作回路の並列処理への回路拡張を行い、その後約2週間で論文執筆を行いました。

2ヶ月で論文投稿するまでに私がやったこと

  • 指導教員と週一のMTGで進捗の報告と、今後の方針を確認する
  • 同じ種類のボードを利用している同期にボードの仕様や実行手順をたくさん質問する
  • 自作回路は問題なく記述できるのに比べて、圧倒的に開発環境のセットアップや回路を動かすまでの細かい設定などが難しかったため、わからないところは2年間今の研究室で経験を積んだ同期に助けてもらう
  • 本番PCがすぐに支給されなかったため、それまで手元のPCで問題を簡単にした自作回路を設計して、記述し簡単なシミュレーションをした
  • 本番ボードがすぐには支給されなかったため、Pynqボードというボードを友人に貸していただき、簡単にFPGAボードへの実装の復習をした(以下の記事がその内容です) taiyaki-future.hatenablog.com
  • 開発時は先生に頼ることが少なく済んだのですが,論文執筆時は用語や書き方などまだよくわからない部分も多かったので時間をかけて何度もコミュニケーションをとり仕上げていきました

今後のよてい

今回のテーマで7月に学内発表、9月に国内発表(今回論文投稿をした学会です)をしてきます。本当にいろいろな方々にサポートしていただいたので、1年で良い結果が残せるように精進します。

まとめ

研究室を変えて、2ヶ月で論文投稿した話でした。卒業まで1年しかない修士2年で研究室を移動すると決めた3月4月は、1年で本当に卒業できるのかどうか、成果が出るのだろうか...とずっと思っていました。この2ヶ月は不安な気持ちがとても大きかったですが、まずは今1つ成果が出せたことに素直に喜んでいます。これで卒業できるというわけではないので引き続き研究を進めていきます。この記事を通してこういうケースもあると少しでも誰かに共有できると嬉しいです。

Deep Learningを学び始めた女子大学院生がCourseraのStructuring Machine Learning Projectsを受けて習ったことをまとめてみた

CourseraのDeep Learningの3つ目の授業が終わったのでまとめです。今回受講したのはStructuring Machine Learning Projectsという授業です。時間的、空間的、金銭的な制約のある中でどのようにデータを選択し、モデルの精度を効率よく上げるためにどのような視点で考察すればいいかを学びました。先生も言っていましたが今回の授業のテーマは実践的な場面で役にたつ技術をまとめたものだったので、私の経験が少ないこともあり、あまり具体的な想像ができませんでした。私にとってはひとつひとつ理解しながら聞くというよりは、素早く本を読むような感じで、将来役にたつかもしれないことをざっくりと頭に入れるという感じでした。

f:id:taiyaki_future:20190516151020p:plain
3つめの修了証

内容

1週目は時間を無駄にしないための、モデルの評価の設定方法の話と、モデルの精度を改善するときにどこを見ればいいかの授業でした。評価として、まずprecision, recallなどの基本的なevaluation metricsの説明がありました。そして、モデルの正しさを測るために、単なる機械的な精度だけでは不十分な場合があることを学びました。

たとえば分類だけで見れば精度がいいモデルがあっても、それがユーザーを不快にさせるような出力をする場合などは、分類の精度が下がっても不適切な出力がないほうが選択されるべきなので、評価方法を変えるべきという話がありました。似たような話は私も聞いたことがあって、ユーザーへのレコメンド機能などはユーザーが不快にならないようにわざと精度を下げる、購買効果を高めるために、ユーザーの検索ワード以外も候補として表示するなどを連想しました。

ゴールにたどり着くためには、モデルがゴールに近い出力を生成するためのメトリクスを探さなければならないし、生成したモデルの評価関数と実際の目的がずれてしまうと再び設定からやり直さなければならず時間を無駄にするということでした。

モデルの改善に対しては、Avoidable bias(正解ラベルとトレーニングセットの予測に対するエラーの差分)とvariance(トレーニングセットの予測に対するエラーとDevセットの予測に対するエラーの差分)で二つのエラーのうち高い方あるいはAvoidable biasが大きすぎる場合はAvoidable biasを優先して改善すればいいということでした。長くなるので説明しませんが、下の写真のノートにはそれぞれの削減に際してどのような方法で対処すればよいかをメモしています。

f:id:taiyaki_future:20190519132134j:plain
avoidable biasとvarianceの削減方法

2週目は学習データが十分に手に入らない場合にどのような追加データを選択すればいいかを学びました。まずtrain/dev/testデータのうち、dev,testは同じ分布にしなければ目標がずれて時間を浪費してしまいますが、trainに関してはランダムに起きる少しの違いは修正されていくという話がありました。よって、trainデータとdev/testデータの分布は必ずしも一致する必要はなく、trainデータが足りないときには似たような構造のデータを学習に使える場合があるという話でした。

たとえば運転時の音声認識のためのモデルを構築するのに十分なデータがない場合は、他のアプリケーションなどで利用している音声データを使ってtrainデータを増やせる可能性があります。使えるかどうかは、trainデータの分布のまま、一部をtrain-dev setとします。そして、dev setとの予測エラーの差分(data mismatch)が小さいかどうかで判断できます。

f:id:taiyaki_future:20190519150854j:plain
data mismatchについて

私はGANやpix2pixを使ってデータを増やそうとする研究があるのを知っていますが、その際も、増やしたデータがtestセットにマッチするかどうかを確かめるべきなのかもしれません。

その他、Transfer learning(似たデータでのモデルを事前に作成した上でそれを目的のデータの学習に利用する)/ Multi-task learning(欠損があるときに他のものも一緒に認識するようにモデルを学習する)/end-to-end deep learning(データが多い時は最初からゴールを目指して学習できる)を学びました。このあたりはまだしっくりと来てない部分が多いので、名前と簡単な説明だけを覚えておこうと思いました。

感想

今回の授業は実践的な問題と対策を説明していく内容だったので、新しい視点を知り、少し考えることができました。最初にも書きましたが、この授業は実務的な部分も多いと感じたので必要になったら戻ってこられるようにメモとして残します。

関連記事

taiyaki-future.hatenablog.com

taiyaki-future.hatenablog.com

ハードウェア記述言語でPYNQボード上のLEDをPWM制御してみた

最近研究室に行ったら友人との話の流れでPYNQボードを貸してもらえることになったので、手始めにハードウェア記述言語であるVerilog-HDLと、統合開発環境であるVivadoを使って自作回路を作った話です。

※※注意※※ この記事は読んだ人が再現できるように細部の手順を細かく説明したものではないです。(もちろんコードは適切な手順を踏めば動きます。)初めてFPGAの記事を読む人にもやったことや背景がふんわりとでも伝わることを心がけて書きました。

f:id:taiyaki_future:20190513135209j:plain
PYNQボード

背景

Pythonで制御可能なFPGAボード

PYNQボードにあるZynqという部品にはPS(CPU)とPL(FPGA)が一つのボード上に搭載されています。配布されている専用のOSをmicroSDなどからブートすることでボード上でJupyterを動かすことができます。そのためPythonで書いたコードで簡単にボード上の部品の操作ができることで2年前くらいに話題になったFPGAのボードです。

発売当時、アメリカのDigilentという会社から学割で1万円しないくらいで手に入り、私の周りの友人たちは学生で欲しい人を集めてまとめて注文し、送料を割り勘する注文方法でお得に入手していたのを覚えています。

今回Pythonを使わない理由

単純に好みの問題です。 Pythonを使うためにはOSをブートしてCPUを動かす必要がありますが、CPUとFPGA上の自作回路を繋げるのは少し面倒なのでPythonは使いませんでした。 配布されているデモ用のパッケージはPythonでの記述に利用すると、FPGAはCPUから送られてくるbitファイルによって既製の回路に固定され、簡単に利用することができます。

せっかくPythonで制御ができるPYNQボードなのでCPUとFPGAを繋げてPython越しの実装もそのうちやってみる予定です。

私のスキル

学部2年から2年間Verilog-HDLを利用した回路記述を経験しました。ハードウェアで三角比の高速計算ができるCODICアルゴリズムとPWM(Pulse Width Modulation)制御を組み合わせて矩形波からSIN波を生成したり、ディスプレイに画像を表示させる回路を作成しAXIバス越しに同一ボード上のCPUからその回路を制御したりしました。

やったこと

PYNQボード上のLEDをPWM制御という方法を利用して4段階の明るさに点灯させます。

環境

  • PYNQ-Z1(Xilinx Zynq)
  • Vivado HL System Edition(研究室からのライセンスを持っているのでそれを利用しました。今回のような小規模回路ならば無償用(HL WebPACK Edition)でもできると思います。)

PWM制御とは

基本的にデジタル信号はHighかLowかしかありません。LEDだと、点灯している状態か、消灯している状態かの2つしかなく、アナログ回路のように電池を直列に繋いで電圧を高くしてLEDを眩しく点灯させるというようなことはできません。

そこで"ある区間"の平均値が明るさであるとして制御していく方法がPWMです。例えば下の例では、LED1は"ある区間"をCLK(クロック)周期5個分とすると、5回ともHigh(1)であるので、平均の明るさは1、LED4では平均の明るさは2/5となります。

f:id:taiyaki_future:20190513180709j:plain
PWMタイミングチャート例

ちなみにこの技術はLEDだけでなく、音声で音の大きさを変える(音色を変える)ことにも利用できます。

実行結果

写真ではわかりにくいですが、右のLEDになればなるほど弱く点灯しています。

f:id:taiyaki_future:20190513143633j:plain
実行結果

コード

module top (
    input wire sysclk,
    input wire [3:0] btn,
    output reg[3:0] led
);

wire RST = btn[0];
reg[2:0] count;

// 0~4を繰り返しカウントする
always @(posedge sysclk) begin
    if (RST) count <= 0;
    else if (count == 4) count <= 0;
    else count <= count + 1;
end

// カウント値に合わせて各LEDの値を変更する。
// ledレジスタの4桁それぞれが4つのLEDを
// 点灯させるかどうかを1/0で表している。
// PWM例: 各countの値で0桁目は一番1の数が少ないので弱い光になる。
always @(posedge sysclk) begin
    if (RST) led[3:0] <= 4'b0000;
    else if (count == 4) led[3:0] <= 4'b1110;
    else if (count == 3) led[3:0] <= 4'b1101;
    else if (count == 2) led[3:0] <= 4'b1010;
    else if (count == 1) led[3:0] <= 4'b1101;
    else led[3:0] <= 4'b1110;
end

endmodule

簡単な操作手順

  1. Vivadoのnew ProjectでボードPYNQ-Z1を指定
  2. Add Sourcesに.xdc,.vファイルを追加
  3. Run Synthesisボタンを押して論理合成
  4. Run Implementationボタンを押す
  5. Generate Bitstreamでbitファイルを生成
  6. ボードの電源を入れ、JTAGモードでFPGAに書込み
    • Open Hardware Manager -> Open Target -> (xc7z020_1を右クリック)Program Deviceで書込み

まとめ

PYNQボードのLEDをハードウェア記述言語で制御してみた話でした。Verilog-HDLは新しい研究室でこれからたくさん使うことになりそうなので、環境含め慣れていきたいです。

javaで値がオーバーフローしないためのlong型とint型の足し算掛け算メモ

気をつけていないと忘れがちなので、メモとして残します。javaでコーディング中についつい大きい値のint*intをやってしまうと値が正しくなかったり、マイナスになったりしてしまうことがあります。これは桁あふれ(オーバフロー)がおきた場合に見られる現象です。 int型は32bit,だいたい109くらい(10bitは210(=1024)≈103です)までの数字しか格納することができません。もし、32bitで表現できる数字以上の値を入れると、32bit以上のbitの値は捨て、下位32bitの値のみが値として格納されるようになります。 なぜマイナスになるかというと、32bit中の上位1ビットは正負の符号を表すようになっており、大きすぎる値はこの上位ビットを反転させてしまうからです。0の時は正、1の時は負を表します。

f:id:taiyaki_future:20190503194722j:plain
intの仕組み

桁あふれはエラーメッセージがでないので、間違っていることに気づきにくいです。特に、長めの計算式を一行で描くときは型に注意しないと値が合わないことがあります。

実験

以下今回用意したコードです。 longに代入する数字はlongを表すLを数字の後につけています。この場合はつけなくても問題ないですが、値がだいたい109より大きい場合、Lなしではコンパイルエラーになります。

f:id:taiyaki_future:20190503185938p:plain
試したコード

以下が実行結果です。 int同士の掛け算、足し算は桁あふれを起こしているのでマイナスになっています。

f:id:taiyaki_future:20190503190215p:plain
実行結果

さらに踏み込んだ実験

さらに踏み込んだ実験として、intとlongの計算順序を変えてやってみました。

f:id:taiyaki_future:20190503195640p:plain
さらに踏み込んだ実験のコード

int型をlongでキャストしたものが正解の値なので、4001000000が正解です。int+int+longでは、値がオーバーフローしてしまっています。int+intまではintとして扱っていると思います。同様の理由で全体をlongでキャストしたものも同じ値になっています。long+int+intだと最初からlong型になってるから正しい値がでているんでしょうね。

f:id:taiyaki_future:20190503195709p:plain
さらに踏み込んだ実験の結果

考えてることメモ

int+int+longでlongが来たときに値を確保するメモリの大きさを変えて入れ直しているのかな。Stringでオブジェクトを作り直すみたいにと思ったので書いておきます。

Deep Learningを学び始めた女子大学院生がCourseraのImproving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimizationを受けて解釈してみた

CourseraのDeep Learning講義の2つ目が終わったので解釈したまとめです。今回の授業はImproving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimizationです。正則化を一切しない最急降下法をもちいたNN(ニューラルネットワーク)をベースラインとして、学習精度を高め、かつ高速に学習がすすむためにどのような工夫ができるかを学びました。

www.coursera.org

ちなみに1つ目の講義の記事はこちら

taiyaki-future.hatenablog.com

f:id:taiyaki_future:20190502011319p:plain
コース2修了証

内容

ニューラルネットワークは多層であればあるほど、モデルの表現力は高くなり、トレーニングデータに適合しやすくなります。いきすぎた学習は汎化性能を失いoverfitting(過適合)またはoverlearning(過学習)といわれます。1週目は、トレーニングセットの正しさ(bias)(小さい方が正しい)と汎化度(variance)(低い方が汎化している)の説明から入り、過学習をふせぐための代表的な手法をいくつか学びました。モデルが複雑になることで過学習してしまうので、反対の発想でいかにモデルを単純にするかを考えます。 L1,L2正則化はコスト関数にペナルティ項を与え、逆伝搬法による隠れ層の重みωの更新時に更新後の重みを0または小さくすることで、モデルを簡単にします。dropoutは各隠れ層で設定した確率で、学習時に使うニューロンを選択することで、使われないニューロンを作り汎化する方法です。L1,L2正則化、dropoutは隠れ層の重みを制限する方法ですが、その他にもData augumentation(画像なら回したものを学習に追加するなどデータを増やす), Early stopping(学習の早期打ち切り)、normalizing(コスト関数の等高線を整える)などを学びました。課題ではnumpyだけでL2正則化、dropout付きのNNを実装して、汎化できているかを視覚的に確かめました。また過学習を防ぐためにHe initializationという初期値の設定方法を学びました。

f:id:taiyaki_future:20190502120936j:plain
バイアス・バリアンス
2週目は最急降下法以外の有名なOptimizerを学びました。最急降下法はバッチ勾配降下法とも呼ばれますが、全てのトレーニングデータを学習に使うためデータ数が大きい場合は特に学習に時間がかかります。そこで確率的勾配降下法(stochastic gradient descent, SGD)でランダムに一つのデータを選んだり、ミニバッチ勾配降下法でトレーニングデータを分割して少ない入力で学習させたりして短い時間で学習させます。これらの方法は勾配降下法の式を変えずに実現しますが、式自体を変え、一つ前の状態だけでなく、数回前のあたいも考慮することでより早く収束するという考え方のMomentum, RMSProp, Adamというやり方を学びました。式は追いましたが、Momentum, RMSPropをとりいれたAdamが一番性能がいいということくらいしかまだ違いとして認識できていません。Adamの欠点は前二つに比べてハイパーパラメータが多いのでチューニングが難しいことでしょうか。

3週目は、バッチ正則化、ソフトマックス関数、Tensorflowの使い方を学びました。 バッチ正則化は少し難しかったのですが、たとえば猫分類器を作ろうとして、トレーニングデータに黒猫とその他の動物しかなかった時、うまく特徴を抽出して黒猫以外の猫も猫として分類できるようにするための制約です。数学的には学習中に隠れ層の入力に偏りが生まれないようになるべく平均的な要素のみを学習できるようにしています。利用すると学習の精度、速度共に向上するようです。ソフトマックス関数は2つ以上の分類に利用される活性化関数です。この関数は出力の和が1になるように各要素の確率を抽出できます。今回の課題で指の形を0〜5に分類するときに利用しました。課題ではついにTensorflowを利用してAdamを利用した分類器を作りました。はじめにインスタンスを作ってからセッションを走らせないと実行されないことが慣れるまで大変だったのですが、なんとか終えました。

感想

今回は英語を日本語に翻訳してくれている動画が少なかったので少し大変でした。わからない部分は動画を止めてネットで調べたり、深層学習系の本や、カステラ、はじパタに頼りつつという感じでした。カステラ本(PRML)は数式や前提知識の問題でまだ全然思うように読めません。買ってもらったり借りられたりと本に恵まれた環境で助かってます:>
ノートの写真に誤字脱字が多くて恥ずかしいですが、動画を見ながら走り書きしたものなので...すみません。

参考になった本

おまけ

3週目の課題で、指で0,1,2,3,4,5を作ったものを分類する課題があったので、例のごとく関係のない画像を入れてみることにしました。

  • 韓国のハート
    人差し指一本を立てた"1"と判断しています。(親指が人差し指と認識されたのかな)
    f:id:taiyaki_future:20190502011305p:plain
    韓国のハート
  • 後輩に教えてもらって練習した今風の若者のハートマーク(綺麗につくるのが難しいです)
    親指と人差し指をくっつけて丸を作った"0"と判断しています。0~5のなかなら私も0だと思いました。
    f:id:taiyaki_future:20190502011310p:plain
    最新のハート

Deep Learningに全く馴染みのない女子大学院生がCourseraのNeural Networks and Deep Learningを受けて解釈してみた

CourseraのDeep Learning Specializationで4コースある中でコース1が終わったので感想と、私なりの授業の解釈を書こうと思います。良いかどうかの判断になれば嬉しいです。CourseraはオンラインでスタンフォードやGoogleBrainチームなどに所属する有名な方々の講義が聞けるというものです。研究室を変えたこともあり、時間に空きができたので、受けてみることにしました。 私が修了したコースは、Neural Networks and Deep Learningです。

www.coursera.org

f:id:taiyaki_future:20190416105231p:plain
コース1修了証

内容

全体を通して深層学習の基本的な仕組みを学びました。4週間のコースで、1週目はまず基礎となる隠れ層がない単純なモデルであるLogistic回帰の解説がありました。Loss function(損失関数), Cost function(コスト関数), Activation function(活性化関数)について学びました。Andrew先生が、各式について、直感的な解説をしてくれるのが面白かったです。そして微分に馴染みがない人や、普段数学から離れたことをしている私のような人のためにderivative(導関数)の説明がありました。これらの導関数は入力された各要素の重要度となる重みを更新するための勾配を計算するために必要です。
2週目は同じLogistic回帰でforward propagation(順伝播)からのback propagation(逆伝播)の計算の仕方の解説がありました。コスト関数はいつ使うのかふんわりとしたイメージしかなかったのですが、back propagationに使うための重要な関数なんだと実際に手を動かすことで理解できました。すごく面白かったのが、2週目の演習課題で、入力画像が猫か猫じゃないかを判断するLogistic回帰のモデルを、TensorflowやKeras,Chainerなどのフレームワークを利用せずに伝搬部分からnumpyのみで作るというものです。CourseraバージョンのJupyter notebookに式を書き込んでいくという感じの課題で、できたモデルの精度はそこまで高くないのですが、最後に自分の好きな画像を判定してくれて、意外と犬と猫を分類できていて楽しかったです。猫っぽい画像を探してきていれてみたりしてました。

f:id:taiyaki_future:20190416124939j:plain
2週目課題概要
3週目は隠れ層を1つ持つ2層ニューラルネットワークの解説と、隠れ層で使う活性化関数の種類の解説がありました。多層にすることで線形分離不可能の分類もできることを学びました。この授業があったので、4週目の2層以上のニューラルネットワークがとてもわかりやすかったです。同じ層のニューロン数が少なくても増えすぎてもダメなことがわかる課題が面白かったです。
4週目は多層ニューラルネットワークの解説と実装をしました。まだ直感的な理解しかできていませんが、層を増やした方が空間計算量が少なくなることを学びました。実装は2層ニューラルネットワークの繰り返し処理で、3週目の応用なのでわかりやすくとてもスムーズに進めることができました。(教材すごい)内容は2週目と同様に猫か猫じゃないかの分類モデルを多層ニューラルネットワークを用いて作成しました。おまけとして、どういう猫でない画像が猫と判断されてしまっているか、また猫の画像が猫でないと判断されてしまっているかを表示できるコードがあり、気になっている部分だったのでしっかり準備されてて感動しました。

感想

  • 授業は英語だけど、なんども再生できるからそこまで敷居が高くなかったです。
  • 修了証の取得と授業料で5000円かかったけど、受けた価値がある楽しい授業でした。
  • とにかく課題が面白い!なんでかなって思うことは全部解説してくれていたし、演習になっていました。
  • Andrew先生がめちゃくちゃ優しい(優しそう)そして癒された。
  • 別で読んでいた深層学習の本で、自分じゃ肉付けできない部分を解説してもらえました。

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

英語でよく知らない内容の授業を受けようと思った

深層学習という大学生の時に漠然と怖くて避け続けてきた授業を、しかも英語で受けるということで、ハードルが高いかなと思ったのですが、7日間は無料なので聞いてみて難しかったらやめようと思いはじめてみました。

私のスキル

  • TOEICは700点ちょっと。Listening - Reading > 100点 と、読むのがとにかく遅い人です。
  • 日常英語は多少リラックスしても聞き取れるくらい。数学の専門用語とかは全然知りませんでした。
  • 私の研究はグラフデータの処理クエリ分割による高速化で、Deep learningはあまり使われていません。

どうして受けようと思ったか

ポケモンLDAをやった後、次は技データではなく、ポケモンの画像で分類をしようと画策してweb上の応用できそうなコードを真似していましたが、簡単にうまく行かず...。さらにその時いろいろアドバイスをしてくれた、普段Tensorflowをよく触っている方の言っていることが、全く理解できず。(本当に申し訳ないほどとても丁寧に解説してくださったのですが)これはある程度の基礎が必要だと考えて探した結果Courseraにたどり着きました。

taiyaki-future.hatenablog.com

感動ポイント

授業によっては日本語字幕があるものもあり、日本語字幕があるものは迷わず利用していました。PCでみると、画像と字幕が別れてしまっているのですが、アプリだと話してる部分に字幕が自動スクロールされていくので、とてもみやすかったです。でもスマホだと画面が小さいので、きっとタブレットで学習するのがいいと思いました笑 字幕は文章がボタンになっていて、文章をタップ/クリックすると、動画がその文章まで移動するので(その技術に)感動していました。
あと、各週の最後におまけ動画で、ニューラルネットワークを思いついた人やGAN(Generative Adversarial Network)を作った人へのインタビューがあって、授業とはまた違った面白さがありました。

料金に関して

1コース4週間が想定されていますが、終わったら次の週をどんどん進めていいスタイルなので、春休みの空き時間を利用して通算2週間くらいで終わりました。最初の7日間はお試しで無料でしたが、私には難しいことも多く、調べながらやっていたので14日くらいかかりました。1ヶ月で50ドルくらいで、払っていれば修了証も手に入るので、Andrew先生に感謝の気持ちも勝手に込めさせてもらいお支払いしました。

まとめ

Deep Learning Specialization講座を受けてみている話でした。深層学習の概要が聞けてよかったのと、課題が面白かったのでたくさんの英文を読むのは時々辛かったけど続けられました。

おまけ

面白かった結果です。多層ニューラルネットワークの課題で、猫か猫じゃないかを判定するモデルを構築しました。Accuracyが80%くらいでしたが、ねこのラテアートは"cat"判定でした笑
人のラテアートは"non cat"でした。

f:id:taiyaki_future:20190416115743p:plain
猫のラテアート

第3回RCO日本橋ハーフマラソン(予選)に参加した話

今更ながら第3回日本橋ハーフマラソン予選に参加してA問題を解いた話です。初めてのハーフマラソンで、よくわからないまま中途半端な時間に参加してとりあえずコードを提出した感じでした。その後に参加者が招待されるリクルート全体の社員LT大会なるものがあり、競技プログラミングを始めてから、初めて参加者に会える機会で嬉しいという感じでそちらもエントリーしました。そのLT大会で「ところでどうやって解いた?」って聞かれるとやばい、と思ったので、A問題だけですが、LT大会前にちょっとでもと思い精度をあげて行きました。

atcoder.jp

ハーフマラソンに参加したときのアルゴリズムは本当にポンコツ(Nearest Neighbor法を利用しました)で、スコアがほぼ最低点の2507だったのですが、巡回セールスマン問題の単純な精度改善アルゴリズムである2-optという方法を参考にして、精度を改善した結果、28万くらいまで上がりました。(全体の順位としてはこれで半分くらいだと思います。)

f:id:taiyaki_future:20190327203116j:plain
A問題の精度

※※※注意 これは高い精度の人のブログではないです こういう解き方でこれくらいの結果になるんだなと指標になれば嬉しいです。 Javaで書いたコードも載せています。

「こうやったらいいよ〜」とか、「ここはどうしてこうなの?」とかあれば、ご指摘とか質問とか大歓迎です:) どうぞよろしくお願いします。

問題概要

200個の都市を1日1都市ずつ、1日の移動距離がなるべく同じになるような都市の巡り方を探索するコードを提出します。つまり、各移動距離が全体の移動距離の平均からあまり離れないようなルートを選択すると良いということです。

方針

  1. まず、適当に選んだ初期位置から、Nearest Neighbor法で貪欲に平均との差が一番小さいノードを採用していくことを全ノードに対して繰り返していき、最初の順番とします。(このまま出すとスコア2507です)
  2. 精度を上げるためにランダムに二つのノードを選びswapします。2-optではノードではなくエッジを選択しますが、実装を簡単にするためにノードでやってみました。
  3. swap前とswap後で評価値が低い(低い方が良い)ものを採用して、採用した順番に対して操作2を行います。
  4. 時間の許す限り2,3を繰り返し、良い評価値のものを採用していきます。

コード

import java.util.*;
class Main {
    static HashSet<Integer> set = new HashSet<Integer>();
    static double[][] d;
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int N = sc.nextInt();
    int[] x = new int[N];
    int[] y = new int[N];

    d = new double[N][N];
    double[][] offset = new double[N][N];

    for(int i = 0; i<N; i++) {
        x[i] = sc.nextInt();
        y[i] = sc.nextInt();
        set.add(i);
    }

    //ノード間距離の平均値を抽出
    double ave = 0;
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<N; j++) {
        d[i][j] = Math.sqrt(Math.pow(x[i] -x[j],2) + Math.pow(y[i] -y[j],2));
        ave += d[i][j];
        }
    }
    ave = ave/N;
        
    // ノード間の平均との差を計算する
    for(int i = 0; i <N; i++) {
        for(int j = 0; j<N; j++) {
        offset[i][j] = Math.abs(d[i][j] -ave);
        }
    }
    
    // nearest neightborで順番を決定
    int[] a = new int[N];
    a[0] = 0;
    set.remove(0);
    for(int i = 1; i<N; i++) {
        a[i] = searchNextI(a[i-1], offset);
    }

    Random rnd = new Random(1000);

    // 時間の許す限りできるだけ多くswapする
    int[] ans = a;
    int cnt = 0;
    while(cnt<600000) {
        int x1 = rnd.nextInt(N);
        int x2 = rnd.nextInt(N);
        int[] b = makeNewArray(ans,x1,x2);
        ans = (calcEval(ans)<calcEval(b))?ans:b;
        cnt++;
    }
    for(int i = 0; i<N; i++) {
        System.out.println(ans[i]);
    }
    }

    // arrayを複製してx1,x2番目のノードをswapしたものを返す
    public static int[] makeNewArray(int[] a,int x1,int x2) {
    int[] b = new int[a.length];
    for(int i = 0; i < a.length; i++) {
        b[i] = a[i];
    }
    b[x1] = a[x2];
    b[x2] = a[x1];
    return b;
    }

    // arrayの順番でのスコアを計算して返す。
    public static double calcEval(int[] a) {
    double val = 0;
    double ave = 0;
    // aveの計算
    for(int i = 0; i<a.length; i++) {
        if(i==a.length-1) {
        ave += d[a[i]][a[0]];
        } else {
        ave += d[a[i]][a[i+1]];
        }
    }
    ave = ave/a.length;
    
    // 評価値の計算
    for(int i = 0; i<a.length; i++) {
            if(i==a.length-1) {
        val += Math.abs(d[a[i]][a[0]]-ave);
            } else {
        val += Math.abs(d[a[i]][a[i+1]]-ave);
            }
        }
    return val;
    }

    // 現在の位置から最も平均的な距離で到達できるノードのインデックスを返す関数
    public static int searchNextI(int curI, double[][] offset) {
    if(set.size() == 0) return -1;
    double min = Integer.MAX_VALUE;
    int minI = 0;
    for(int i = 0; i<offset.length; i++) {
        if(curI != i && set.contains(i)) {
        if(min > offset[curI][i]) {
            min = offset[curI][i];
            minI = i;
        }
        }
    }
    set.remove(minI);
    return minI;
    }

}

感想

今回は移動距離の分散が小さくなるようにという制約だったのでよくわかりませんが、2-optのようにエッジをswapするのではなく、私が採用したノード二つをswapするやり方だと、交差が増えるもしくは減らないことが多いので移動距離の総和が評価値の時には使えないなと思いました。
TLEのときの申し訳なさは半端ないです。途中で止めれるような機能があったらいいと思います。 問題2についてはLT大会前に確認しましたが、これはもしやDFS..!と思ってそっ閉じしました。土日とかで時間があるときにやってみる予定です。

LT大会に関して

私が参考にしたことがあるサイトの著者さんとtwitterを交換できました。競技プログラミングの話はあまり多くの人とできていないので、とても楽しかったです。リクルート全体で開かれたLTの内容は就活生向けで会社紹介や環境紹介のものが多かった印象でした。(就活生に声がかかったようですね)私はもう少し技術的な話の割合が多くてもよかったかなとも思ったけど、仕事の成果物のデモとか見せてもらえたのが面白かったです。あと個別で社員さんとずっと興味のあった技術の話が聞けて面白かったです!