リアルタイム感情認識をしてみた
背景
いろいろあって以下のリポジトリの感情認識を使おうと思いました。Raspberry Piとかと組み合わせてIoT的なことをしたいと思っていて、感情ごとに出力がかわるといいなと思っています。ローカルで試しました。Macの内蔵のカメラを使ってデモコードを動かすことがゴールです。
リポジトリ概要
こちらが論文です。まとめるとCNN使って性別と感情を認識できるようにしたよという論文です。 出力は{"angry", "disgust", "fear", "happy", "sad", "surprise", "neutral"}の7種類です。今回はリアルタイムに表情認識がしたいのでリポジトリの中でも> src/video_emotion_gender_demo.pyだけ見れば大丈夫です。ちなみにtrained_modelsとかは学習済みのパラメータが読めない形で入っていたり、imagesはテスト用の画像(おそらく)が入っていたり、datasetsはローカルでデータ入れるようのフォルダだったりするので、実質srcフォルダのみを追います。
コードの解説
video_capture = cv2.VideoCapture(0) ここで内蔵のカメラとか接続したカメラから画像をよみとっています。あとはemotion_label_arg = np.argmax(emotion_classifier.predict(gray_face))でモデルにむかって推測してねを投げています。emotion_label_argに0,1,2…のラベルの数値が入っていて、emotion_textは「neutral」とか「happy」とかが入っています。このあたりを使えば感情ごとに何か制御できそう。
環境構築でつまづいた話
ちなみにリポジトリにはこのリポジトリは非推奨で、新しいTensorFlow2系で書いた方を使ってねとありますが、pipでインストール形式になっていてコードリーディング がしにくかったです(能力不足)。そのため旧リポジトリでゴリ押ししようと思いました。
という環境でREQUIREMENTS.txtを片っ端からバージョン無視してインストールしても動きました。 ただ、バージョンを揃えようとするとopen sslのエラーにぶちあたったり、opencvでエラーが起きたりしました。
上記の方針でもうとにかく自分のバージョンでいくぜ!としたのでTesorFlowも結局2.5です。 一応バージョンそろえたpip freezeをgistにあげます。
face_classificationのrequrements.txt · GitHub
あと気にするべきことは以下です。
- Macのカメラアクセスを設定から許可する必要がある
- OpenCVのビルド時にVideo I/OのFFMPEGがYESになっている必要がある
- cv2.VideoCapture(0)の0のところは-1とか1の可能性もある(自動でカメラに割り当てられているみたい)、特にこの場合は明確にエラーが出なくてわからなさそう
OpenCVのビルド情報については
import cv2 print(cv2.getBuildInformation())
で確認できます。
これで無事に感情認識ができるようになりました。やったね
(競プロ)abc023 D問題
今日から
というリポジトリの問題を使って典型問題のとき方に触れつつ、類題で具体的なコードまで理解するということをします。ABCのD問題の難易度も多々あり、90問あるので終わったら自力回答が多少できるようになっているのでは??
D問題概要
射撃王です。
とき方
github.com こちらのリポジトリの1つめの解説を参考にすると、考え方の概要がわかります。 ざっくりかくと「できる/できないの境界がある場合のスコアの最大/最小値」を求める場合、できるかできないかで二分探索をする(スコアk+1は可能だが、スコアkは無理、という値kを見つければ良い。)です。
今回の問題だと
- スコアXに対して二分探索をして、どこまでがとける問題かを探る
- 1つずつが可能かどうかは風船ごとにどれくらいのスピードで割らなければいけないかを考えて、次の1秒で割らなければいけないものが複数になったらとけない問題とすることにする(貪欲法で境界値を探る)
という流れになると思います。
コードに落とし込む
ときかたとしては
こちらの記事が参考になりました。
pythonのコードは
ほぼこれを写経して、今回の二分探索の中で判定をしていくという流れを理解しました。
お気持ち
1周目はとにかくハードルを低く、CとD問題をまず読もうと思っていますが、自力実装までの溝が大きくて心が折れそうです。そういうのも含めて継続できるようブログに記していくぞ。
(競プロ)abc023のC問題を題材に勉強の仕方メモ
背景
Androidエンジニアになると言いましたが、実は状況的にアルゴリズムの実装力が必要です(転職と研究)。何度かチャレンジしては勉強の仕方がわからん…となっていた競プロをちょくちょくやろうと思います。特に最初の一歩って「AtCoder使おう」「問題集解こう」という記事が多くて、私のような弱いエンジニアはAtCoderは使えないし問題集は解けないしで幅が広がりません。そういう初心者(?)さんの取り組み方の助けになれば。
そもそもの勉強方針
方針としては
- 過去問に触れて典型パターンを知る
- 勉強は基本C,D問題(AtCoder Problems)を解く
- だいたい解けないので「手法の理解」と「コードの理解」にわけて勉強し、1周目は理解、2周目以降で自力で解けることを目指す
- あと細かい手法や基本的な考えの補完にleetcodeも使うかも
みたいな感じを想定しています。
abc023のC問題概要
収集王です。公式解説にあるように、この問題は典型的考え方(二分探索とかハッシュテーブルとか)というより、パズルみたいに問題を解けるか系の問題に見えます。こういう問題は「解説に近そうな他人のコードを持ってくる」「デバッガを使ってあるinputのときの変数を追いかける、特にテーブル作る系は手元に図を書く」という方法で読むと読めるかもしれません。
ときます
# for test R, C, K = 3, 5, 3 N = 5 ameInfo = [[1, 2], [2, 1], [2, 5], [3, 2], [3, 5]] # 行列と飴の個数 # R, C, K = map(int, input().split()) # 空行くぎりのstringで渡ってくる # N = int(input()) # 飴情報の個数 # ameInfo = [list(map(int, input().split())) # for i in range(N)] # N行分空行くぎりのstringがくる # 飴マップを作る row = [0]*R column = [0]*C for x, y in ameInfo: # 飴座標1つずつについて、飴マップに情報を入れる。マスはインデックス+1なので-1に入れる row[x-1] += 1 column[y-1] += 1 answer = 0 # カウント # ここだけわからない rowx = [0] * (K+1) # Kはターゲットの飴の数 [0,0,0,0]みたいなのができる columnx = [0] * (K+1) for rowi in row: if rowi <= K: rowx[rowi] += 1 # K個以下獲得する場合、飴の個数インデックスのところに+1。0もある for columni in column: if columni <= K: columnx[columni] += 1 for i in range(K+1): answer += rowx[i] * columnx[K-i]#K個飴を取得組合せ。i=1でK=3ならrowから1個、columnから2個とれる場合…をかけている # 余剰分をのぞく for x, y in ameInfo: if row[x-1]+column[y-1] == K: answer -= 1 elif row[x-1]+column[y-1] == K+1: answer += 1 print(answer)
C問題は解説読んで分かった気になったものの、コードにするの難しいよーと思ったので読めるコードを探しました。こんな感じで読めるコードをみつけたらVSCodeあたりで動かします。test用に一番シンプルなインプットを下手書きしちゃうといいと思います。競プロのコードは変数名とコメントの問題で可読性が低いので、ゴリゴリコメントをつけました。
今回のコードだと# カウントのコメントのあとくらいのロジックがわからなかったので、デバッグして追った結果が#にあるコメントです。配列の中がどうなっているのか、何をカウントしているのかをループごとに追います。手元に変数ごとの状態のメモを書きつつ追っています。
このあとどうするか
この手の問題は類題を解くというより発想の問題な気がするので、時間を置いた時に自力でコードがかけるかを試したいです。というわけで、1週間後のカレンダーに問題番号をメモして再チャレンジします。
今後の勉強
Androidなどの自分が手を出したい勉強の他に、ちょっとした仕事前の導入くらいな気持ちで取り組めるようにまずは心理ハードルを下げます。仕事前にC問題くらいの難易度を1問解いていこうかと思います。え?コンテスト?弱いし今参加したら心折れちゃうのであくまでも頭の体操として過去問を解くんです
5ヶ月でAndroidエンジニアになるという気持ち
背景
Webエンジニアはやめません。いろいろ面接を受けたり人の話を聞いていて、体力のない私は業務系のエンジニアに全振りするのも難しいと思いました。(あと体力りいそうなゲーム系のエンジニアも)。一方でゴリゴリアルゴリズムを実装したい気持ちはあって、自分の知識を実装できてゴリゴリかけそうな分野のAndroidに手を出します。知っています、やりたいことはWeb系でもできます。Webを極めるべきだと私も思います。それでも夜間のクラスメイトがAndroidでちょろっと何か作っていたり、iotデバイスと連携したりするのを見て、これだと思ってしまったのです。また、効率的に生きていたいので成功体験がほしいこと、時間のコントロールの練習でもあります。
何をするか
- [ ] Anrdoid Kotlin Fundamentalsをやる
- [ ] 上のAdvancedがあるのでやる
- [ ] Androidアプリ開発パターン入門をよみ、TODOアプリを自力で作る
- [ ] 上につまずいたらじゃんけんアプリか占いアプリあたりから始める
- [ ] ナッシュ均衡使ったアプリつくる(多分ランチ決めるやつ)
- [ ] マルコフ連鎖ではげましてくれるアプリをつくる
いつ勉強するか
実は5ヶ月というのも研究室の忙しさが落ち着く頃にあわせたスケジュールです。体力がない私は仕事を効率化し、仕事以外の時間は仕事のことを考えなくてもいいくらいの生産性を持つこと、仕事で疲弊しないことが最重要です。1日9時間くらい寝ても15時間くらいあって、9時間仕事、1時間研究しても5時間あります。生活諸々があるにせよ平日1〜2時間、昼休み、休日は時間を捻出できる計算です。理論上はいけそう。
工夫としては - [ ] ポモドーロタイマーを使って仕事中は工数計算をしっかりする、タスクを25分レベルまで落とす - [ ] 仕事以外の時間も一応タイマーは使ってどれくらい進めるかを進捗管理 - [ ] 仕事環境を整える(椅子をゲーミングチェアに、モニター2枚に、部屋の片付け) あたりをしてみようと思います。
目標は何か
目標は大きく2つあって - 仕事を効率化して自由時間をふやす - 時間のコントロール力を身につけて自由な時間を作ること - タスクの細分化ができるようになること - 自分で1から勉強して副業を始めること - 成功体験をふやす - 単純に収入をふやす です。
上記プランは理論上可能だけど現実はどうなのって感じになっているので、その差分から目を背けない、諦めないためにもブログを書きました。
来年の目標(と、研究室に配属された話)
前回に引き続き、入院と救急車を繰り返す病弱人間でした。元気になってきたのでまたアウトプットを頑張ろうかと思って、年末も近いので簡単に今年をまとめつつ来年の目標についてお話しします。理系の大学に興味のある人もいるかと思うので、大学生活の中でも一大イベントの研究室配属についてもお話しします。
今年のまとめ
- 4月に眼科系手術、9月と12月に救急車&入院騒動、休職
- 大学学部授業ほぼ終わり
- 仕事はほとんどできず。エンジニアとしてはPythonを好きになった
- フロント・webより業務系の仕事に興味を持ちwindows(GPU載せ)を購入
来年の目標
来年の目標からは年の終わりにできたできないを反省できるようにしたいです。今は20代も後半ですが、同じように病気抱えてキャリアに悩んでいる人、理系コンプレックスな人が少しでもこんな生き方もあるんだと希望に感じてくれると嬉しいなと思っています。
病院に通って通勤できるくらい元気になったら業務系に転職する
言わずもがな。業務系というのは、ずっとWebサービスを開発していてコーディングに不安を感じていました。授業の中で自分で1から考えて、何も参考にできるものがない中シーケンス・クラス・ロジックを考える機会があったのですが、MVCのフレームワークを使ったりするのと違ってだいぶ自由にコーディングできました。私が今まで不安だったコーディング力は、自分でシーケンスを考えたり、低レイヤーも考慮できたりという力で、コーディングを手段と考えられない自分にとって今の働き方は遠回りに感じています。Webサービス界隈でも自分でライブラリを作ったり中身をみたりできる人もいると思いますが、Cやアセンブリはやる気になってもPythonやReactではそういったことはできませんでした。向き不向きが見えたので、いったん違った領域を試してみようかと思っています。
適切な発信をする
今までは病気、キャリアに悩む、というマイナスの面が大きく、自分のやってみたことも少なくて、ポエムな発信しかできていなかったと思います。今年始めにブログのコーチングを受けたのもあり、今後は自分のやってみたことを増やし、特に理系に興味のある人、自分に向いた働き方を模索している人に向けて届けられたらと思います。また、私は普段結構柔らかい話口調なのですが、文字になると長くてかたくなってしまいがちです。完全武装モードを解いてとっつきやすく発信していきたいです。
研究室で成果を出す
そういえば、研究室配属をされました。その話は後ほど。
研究室に配属された話
Twitterで交流のあった方から、スケジューリングに困っているという話を伺ったことがありました。元々離散数学が好きだったのに加え、そういったいわゆるソルバーを使って解くスケジューリング問題ってなんだろうというところから数理最適化にたどり着きました。何もわからない中で研究室に配属され、今数理最適化の全体像を把握しようと頑張っています。このあたりの解説記事も書こうと思っています。今知っている限り、身近なところだと路線検索アプリやマッチングアプリ、災害時のサービス復旧の計画などに使われているようです。面白そうでしょ。
以上、来年の目標でした。来年は生きている実感がほしいです。また来年もよろしくお願いします。
windowsのキーバインドを変更する
最近体調を崩して救急車で運ばれるだとか、それとは別に手術するだとかしていました。web屋さんなのでMacを使っていたのですがこの機会にやりたいことを見つめなおそうと思いとても久しぶりにwindowsを購入し、キーボードはおさがりのゲーミングキーボードを使うことにして、久しぶりにブログを書こうとしたんです。 そこでwindowsキーがないことに気づきました。windowsでスクショを簡単にとるにはwindowsキーが必要です。長くなりましたが、初めてキーレジストリをいじってキーをマッピングすることにしました。
やること
キーボードにあるよくわからないキー(見たこともない図が書いてある)をwindowsキーとして割り当てる
手順概要
yukituna.com こちらのサイトを参考にしました。
- キーに割り振られた値を確認する
- レジストリエディタを開く
- Scancode Map というファイルを作成する
- 変更したいキーのバイナリを書き込む
- PCを再起動する
キーに割り振られた値を確認する
kts.sakaiweb.com Keymillというソフトを使用しました。動作に.Netの環境が必要なようです。 起動すると下記のような画面になります。 ここで注意したいのは必要なキーの値はスキャンコードのところなのですが、16進数で必要です。 デフォルトは10進数になっているので設定から変更します。 設定後、左上の「記録開始」ボタンを押すと押下したキーの情報が記録されます。
レジストリエディタを開く~PC再起動まで
yukituna.com さきほど紹介した上記のサイトに沿って設定を行っていきます。 バイナリの書き方だけは
so-zou.jp このサイトを参考にしました。
キーの値を各順番に注意が必要で、ビッグエンディアンなので先ほど確認した値を2桁ずつ逆にして書きます。 レジストリエディタにScancode Mapというファイルを作成し、無事にバイナリを書き込めたらPCを再起動しましょう。晴れてキーバインドが変わっているはずです。
感想
正直windowsを触るのも久しぶりで、PCを触るすら久しぶりで1時間ほどかかってしまいました。今後はしばらくweb系に関係のないもの、例えばOS作ってみるとかCPU作ってみるとか、時にはFPGAを触ってみるとか、そういうことで息抜きをしつつ回復しようと思います。
Pythonで関数を呼び出す時に参照渡しで呼び出し元の変数を直接関数の中で書き換えたかった
Pythonで関数を呼び出す時の引数を参照渡し
にして、呼び出し元の変数を直接関数の中で書き換えたかった時に躓きました。イミュータブル
が良いとされている世の中ですが、どうしても参照している値のインデックスを呼び出し元の変数と直接関数の中とで共有したかったという状況です。この悩みの原因はそもそもPythonで関数を呼び出す時に参照渡し
になっているところにあるのですが、同じように悩む人がいるかもしれないので詳細な仕組みについてまとめます。
参照渡しと値渡し
値渡し
が引数の値をコピーして渡す方法で、独立してメモリ上に値を保管します。参照渡し
がメモリアドレスを渡す方法で、呼び出し元と関数の中で同じ値を参照します。
Pythonのミュータブルとイミュータブル
ミュータブル
な値はリスト、セット、ディクショナリなどです。部分的な値を変更が可能(メモリ上の値自体
の変更が可能)です。
イミュータブル
な値はタプル、数値型、文字列、ブーリアン、Noneなどです。注意するべきは、イミュータブル
な値でも再代入
は可能なことです。仕組みとしては、変数にメモリの番地
が入っており、再代入するとメモリの番地
が変更される、ということになります。一応コードで具体例を示します。
a = 5 b = a #この時点ではメモリの番地は同じ b = 1 #ここでbのメモリの番地が変わる
Pythonは変数にメモリの番地
を持っているため、値渡し
と参照渡し
という切り口ではなく、ミュータブル
とイミュータブル
と言う切り口で捉えるのが適切です。Pythonではメモリの番地
を直接操作するような低レイヤの事柄は基本的に見えません。
ここまででわかるように、私が悩んだ
呼び出し元の変数を直接関数の中で書き換えたかった
はPythonではできないようです。