ぴよまるの雑多な技術所

間違えて休職中ってずっと書いていて心配されていました。全部ちょっとずつ齧って好きって思える技術を見つけたい。研究分野は数理最適化。

リアルタイム感情認識をしてみた

背景

いろいろあって以下のリポジトリの感情認識を使おうと思いました。Raspberry Piとかと組み合わせてIoT的なことをしたいと思っていて、感情ごとに出力がかわるといいなと思っています。ローカルで試しました。Macの内蔵のカメラを使ってデモコードを動かすことがゴールです。

github.com

リポジトリ概要

こちらが論文です。まとめると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問題

今日から

GitHub - E869120/kyopro_educational_90: 2021/3/30 ~ 2021/7/12 に行われる企画「競プロ典型 90 問」の問題・解説・ソースコードなどの資料をアップロードしています。

というリポジトリの問題を使って典型問題のとき方に触れつつ、類題で具体的なコードまで理解するということをします。ABCのD問題の難易度も多々あり、90問あるので終わったら自力回答が多少できるようになっているのでは??

D問題概要

射撃王です。

とき方

github.com こちらのリポジトリの1つめの解説を参考にすると、考え方の概要がわかります。 ざっくりかくと「できる/できないの境界がある場合のスコアの最大/最小値」を求める場合、できるかできないかで二分探索をする(スコアk+1は可能だが、スコアkは無理、という値kを見つければ良い。)です。

今回の問題だと

  • スコアXに対して二分探索をして、どこまでがとける問題かを探る
  • 1つずつが可能かどうかは風船ごとにどれくらいのスピードで割らなければいけないかを考えて、次の1秒で割らなければいけないものが複数になったらとけない問題とすることにする(貪欲法で境界値を探る)

という流れになると思います。

コードに落とし込む

ときかたとしては

scrapbox.io

こちらの記事が参考になりました。

pythonのコードは

note.com

ほぼこれを写経して、今回の二分探索の中で判定をしていくという流れを理解しました。

お気持ち

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デバイスと連携したりするのを見て、これだと思ってしまったのです。また、効率的に生きていたいので成功体験がほしいこと、時間のコントロールの練習でもあります。

何をするか

いつ勉強するか

実は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 こちらのサイトを参考にしました。

  1. キーに割り振られた値を確認する
  2. レジストリエディタを開く
  3. Scancode Map というファイルを作成する
  4. 変更したいキーのバイナリを書き込む
  5. PCを再起動する

キーに割り振られた値を確認する

kts.sakaiweb.com Keymillというソフトを使用しました。動作に.Netの環境が必要なようです。 起動すると下記のような画面になります。

f:id:let_piyomaru:20201018005911p:plain
Keymillの起動
ここで注意したいのは必要なキーの値はスキャンコードのところなのですが、16進数で必要です。 デフォルトは10進数になっているので設定から変更します。
f:id:let_piyomaru:20201018010121p:plain
keymillの設定
設定後、左上の「記録開始」ボタンを押すと押下したキーの情報が記録されます。
f:id:let_piyomaru:20201018010146p:plain
keymill記録

レジストリエディタを開く~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ではできないようです