はじめに
はじめまして。
2021年12月から某社のプロジェクトに長期インターンとして参画し、先輩の方々に大変なご助力を頂きながら機械学習に携わる業務を進めてきましたが、参画から4ヶ月経ち、次第に知識のインプットやPCの設定等、詰め込み作業が一段落してきた雰囲気がしています。
また、最近ありがたいことに新規インターンの方をメンタリング(?)するようなことも増えてきて、自分がプロジェクトに入った直後のことを話させて頂く機会もあるので、せっかくなら自分なりのプラクティスを記事にしてパパッと読んでもらおう、と考えました。
ということで、この記事では今までに知ったことと、知ったことのうち、作業環境を整えるためのことの内容をまとめます。
気づいたら想像の5倍ぐらい長文になってしまいましたが、新人MLエンジニアの方々の参考になれば幸いです。
目次
MLエンジニアを始める前の環境
著者の状態
- Python歴半年 (大学で基礎の基礎だけやった直後)
- 線形代数・微分積分・統計の基礎だけ分かる
- Gitの使用経験(ほぼ)なし
- pandas, tensorflow, pytorch 等、名前も聞いたことがない状態
- 機械学習と深層学習の違いが分からない
PCの状態
- Intel Mac (5年前のモデル、2022年1月に壊れる)
- M1 Mac (2022年1月-現在)
今までに知ったこと
以下の2つを軸に書いていきます。
- 知ったこと
- 知ったことのうち、作業環境を整えるためのこと
後者の知ったことのうち、作業環境を整えるためのことについて、具体的には作業効率化のために調べ、導入しては消してを繰り返した結果生き残ったアプリを紹介します。
なお、筆者の環境からMac OS Xのみ導入できるものが数多くありますが、Windowsでも代替アプリがあるものは記載していますので、ぜひご覧ください。
知ったこと
まずは、先にこの4ヶ月意識して知ろうとしなかったことを述べておきます。
-
MLの数理的な深い背景知識
- 怒られそうですが、TensorflowやPyTorchのAPI仕様を理解する方が先です。
- また個人的に、数式を数式で見て理解するより、数式のPythonによる実装を見たあとで数式を理解する方が1000倍楽なので、やはりPythonの解読能力を上げる方が先です。
-
Python以外の言語
- 大学の授業でCとJavaを触りましたが、キャパオーバーを避けるため深掘りを避けました。
-
MLの新しい手法
- 機械学習の新しい手法って大体が分厚い文脈の上に生まれていて、基本的にその文脈を飛ばして理解しようとすると躓きます(1敗)。
- その上、技術として枯れていないので分かりやすい解説記事もありません。重すぎるので後回しにしました。
そして、4ヶ月の間に知ったことは大きく分けると以下の4つになります。
- コードの書き方に対する知見
- アプリケーション、PythonライブラリやGitといったツールに対する知見
- MLへの薄っぺらい知識
- 職場での円滑なコミュニケーションに対する知見
それでは、1つ1つ順を追って書いていきます。
もし、それぞれの知識のソースを覚えていれば直後に載せています。
1. コードの書き方への知見
この項目で特に大事だと思ったものは以下の3つです。
- 人が理解しやすいコードを書くこと
- PEP8に(ある程度)従ってコードを書くこと
- コードについて思ったことはコードの中にコメントすること
人が理解しやすいコードを書くこと
1人でコードを書いていると、しばしばコーディング方針の優先順位がコード行数の少なさ > 理解しやすさになり、人間の理解を拒むようなリスト内包表記を書いては「すげえコード書けたな」「天才か?」と悦に入ってしまうことがありますが、絶対に良くないです(999敗)。
自分の過去のコードから例を挙げます。例えば、こんなコードです。
df = pd.DataFrame(
{
"beta": beta,
"alpha": np.mean(Y) - (b[0] * np.mean(X[:, 0]) + b[1] * np.mean(X[:, 1])),
"R^2": 1-(((np.mean([abs(y-((np.mean(Y) - (b[0] * np.mean(X[:, 0]) + b[1] * np.mean(X[:, 1])))+(b[0] * x[0] + b[1] * x[1])))**2 for x, y in zip(X, Y)])))/np.var(Y)),
}
)
確か、線形重回帰をやってその結果をデータフレームにしたかった時のコードだったと思います。右にスクロールするとようやく全貌が見えますね。
このコードを書いたとき、何か自分のコーディング能力に関して「フフン」みたいな気持ちを覚えた気がしますが、今見るとこのコード書いた人と一緒に仕事したくないという思いしかないです。
理解しにくいコードを書くことが良くない理由として、当然ですが、「他人から理解の時間を余計に奪ってしまうこと」があります。
もう1つ、「将来の自分が困るから」という理由もよく耳にしますが、自分の場合は「コードの挙動を他人に聞かれたとき、説明が難しいから」の方がしっくりきます。
コメントを残しておいて難しいコードの理解を補助するのも手ですが、リーダブルコードにもあるように理解しやすいコード > 理解しにくいコード + 良いコメントなので、自分は頭を捻って分かりやすいコードが書けるように頑張っています。
PEP8に(ある程度)従ってコードを書くこと
Python固有のコーディング規約の話をしてしまいますが、これも結構大事です。
PEP8というのは、かなりざっくり言うとPythonの書き方のルール(変数の命名法則とか)の集合です。
公式ドキュメントはこちらです。結構な分量があります。
とはいえ、このルールを守らないとエラーが起きてしまうわけではありません。
しかし、ほとんどの人がPEP8に(割と)従ってコードを書いています。
PEP8のルール1つ1つに正当性があるのかは別として、みんなが似たようなルールに従ってコードを書いているというのはかなり嬉しいことで、それだけでコードが読みやすいです。
例を挙げましょう。
日本語の文章でも、段落の始めには1文字分空白を空けるとか、文頭に句読点を置かないとかいうルールがありますが、別に守らなくてもいいですし、段落始めの行頭の1文字空白に必然性とかは多分無くて、2文字空白でも良かったと思います。
しかし、みんながそのルールを守っていることで何が嬉しいかというと、色んな書籍を読む際にいちいち著者のインデントの文字数の癖とかを把握しながら読む必要が無くなります。
本題に戻ると、PEP8を守ることでコードからエンジニア1人1人の書き方の癖を省き、コードリーディングに発生する無駄なコストを減らすことができるということです。嬉しくないですか?
とはいえ、PEP8に書いてあることを全部覚えてから作業に取り掛かるのはあまりにも高コストなので、VSCodeにフォーマッターを導入しましょう。これを入れると、.pyファイルのセーブ時にコードをPEP8(ほぼ)準拠のフォーマットに自動補正してくれます。
ちなみに、フォーマッターについて、何も弄らなくてもかなり良い感じになるという点でblackがおすすめです。
導入方法についてはQiitaでも色々ヒットしますが、自分はこの記事を見て設定した気がします。
コードについて思ったことはコードの中にコメントすること
地味に大事なことです。
めちゃくちゃ雑ですが、例を挙げます。
def divide(x: int, y: int) -> int:
"""小数点以下切り捨ての除算を行う"""
return x // y
あなたはこのコードを業務中に書き上げたものの、寝る前にふと見直してみると重大な欠陥に気付きます。
(y=0が入るとマズい...)。
ゼロ除算の可能性に気づいてしまいました。
しかし、今は眠気の限界です。どうしても例外処理を入れる気力がないので、明日修正しよう、と心に決めて眠りにつきました...。
ということをしてはいけません。
一分一秒を争う状況でもない限り、5文字だけでも書き残しておきましょう。
# XXX: 0
def divide(x: int, y: int) -> int:
"""小数点以下切り捨ての除算を行う"""
return x // y
別になんでもいいです。
# y=0がヤバい!!!!!!!!
def divide(x: int, y: int) -> int:
"""小数点以下切り捨ての除算を行う"""
return x // y
書き残しという意味では、コードを書いているときにメモ書きを残すのも重要です。
下の例は過去に自分が遭遇した謎エラーの対処をした際、残した文章です(実際から改変しています)。
# FIXME: int型の要素を挿入したはずの列の要素がnumpy.ndarray[int]型になっている(原因不明)
# dfのカラムaに挿入している要素はintだが、
# なぜかdf.aの要素1つ1つがnumpy.ndarray[int](配列の要素数1)に
# 変化してしまうことがある
# HACK: ナイーブソリューションとして、numpy.ndarray[int]が要素の場合はndarrayから中身を取り出す
if not isinstance(df.a.values[0], np.ndarray):
hoge = df.a.values[0]
else:
hoge = df.change_date.values[0][0]
実装部分だけを見ると、このif分岐は何のためにあるのか全く理解できません。
しかし、このように文脈を残しておけば、誰でも理由を把握できますし、他の人がこのコードを解読する際の大きな助けになります。
もしかしたら誰かが謎エラーを解決して、うまいやり方に改変してくれるかもしれません。
ヴォイニッチ手稿の著者にも、相手に伝わる言語で説明することの素晴らしさを伝えたかったです。
その他
他にも色々ありますが、キリがないので残りはパパッと書きます。
-
関数には型アノテーションを書くこと
- バグが見つかりやすくなります。
-
Docstringを書くこと
- VSCodeならこの拡張機能を入れると便利です。
-
関数・メソッドの機能はできるだけ分割して別々に定義すること
- エンジニアとしてアサインされてすぐにこの教義を知り、その理由を色々と拝見しましたが、当時は全部しっくり来ていなかったので理由は説明しません。ただ、後々分かってくるので今は頭の片隅に入れておくのが大事です。
-
Jupyter Notebookではmarkdownでリッチな説明を書くこと
- 大事な部分で行頭にシャープ(#)を2つ3つくっつけてみるだけでもだいぶ違います。
参考書
先ほども挙げましたが、リーダブルコードは非常に良いです。軽く読めるのでおすすめ。
また、個人的に本当の初心者にはおすすめできないと思っていますが、UNIXという考え方も名著です。ある程度コーディングに慣れてきたらかなりおすすめ。
2. アプリケーション、PythonライブラリやGitといったツールに対する知見
この項目で、個人的に大事だと思うのは以下の3つです。
- エラー文をきちんと利用し、考えられる原因を1つ1つ潰していくこと
- ショートカットキーを覚えること
- コマンドラインに慣れること
エラー文をきちんと利用し、考えられる原因を1つ1つ潰していくこと
これはPCと向き合う上で必須の知識でもありますし、気持ちでもあります。
英語が難しくてやだ、という気持ちはよく分かりますし、実際頑張って読んだからといって意味が分からないことも多くあります。
なので、まずはエラー文を理解する前にエラー文をそのままコピペしてググってみましょう。
もしくは、遭遇したエラーの状況をできるだけ頑張って日本語にして検索します(pandas df loc スライス エラー
とか)。
これで割と解決します。
しかし、残念ながら解決しない場合もあります。
その場合は、何度も同じコマンドやコードを実行しては同じエラー文が出てくることにイライラするのではなく、冷静にエラーが出ている原因の切り分けを行いましょう。
例えば、下のコードでエラーが出たものとします。
hoge_dict = {}
for i, elm in enumerate(a_array):
hoge_dict[elm] = a_array[i - 1]
ここでエラー文が参考にならないと仮定し、考えられるエラーの原因と、その対応策を考えてみます。
- "elm"の型の問題?
- 対応策: エラーを出している要素の型を確認してみる ->
print(type(elm))
- 対応策: エラーを出している要素の型を確認してみる ->
- リストのインデックスが範囲を超えていないか?
- 対応策:
i-1
がとる値の範囲とa_array
のインデックス範囲を確認してみる
- 対応策:
こうして1つ1つ試してみると、8〜9割のエラーは解決します。それでも解決しなければ、
# TODO: エラーの原因が分からない。あとで先輩に聞く
とでも書いておけばいいです。結局のところ、経験のある人に聞くのが早いです。
しかし、ただ答えだけを聞くのではなく、経験のある人がエラー解決の際に見ていたものは絶対に盗みましょう。
ショートカットキーを覚えること
VSCodeにせよ、PyCharmにせよ、ものすごい量のショートカットキーがあります。
覚えるの面倒だし、マウスとかトラックパッドで出来ることをわざわざキーボードでやらなくてもいいし...と思ってCtrl + Z
とかCtrl + C
みたいなメジャーなショートカットキー以外は敬遠しがちですが、他にもショートカットキーを覚えれば、
こんなことも、
こんなことも、
こんなこともできるようになります(VSCodeの場合)。
また、IDEに限らず、普段使っているアプリケーションには大体ショートカットキーがあります。例えばYouTubeとかすごいです。
ある程度経験を積んだエンジニアの方にとっては当たり前のことだと思いますが、知っているのと知っていないのとでは作業効率が段違いなので、一刻も早く知っておくべきだと思います。
余談ですが、ショートカットキーの記憶補助ツールとして、Macの方はCheatSheetが便利です。
コマンドラインに慣れること
ある程度早い段階から使用を避けられなくなってくるのがコマンドラインです。
しかし、いかんせん学習コストが高く、見た目からしてとっつきにくい...。
man <コマンド>
と<コマンド> --help
(各コマンドの説明書を表示するもの) さえ覚えてればなんとかなる、と言いたいところですが、3ヶ月前の自分にそう言っても無視されると思うので、黙っておすすめの書籍を紹介します。
自分はこの書籍を最近読みましたが、早く読んでおけばよかったと後悔しています。
コマンドラインは古くからあるものなので、他にも良い解説書はたくさんあると思います。
ただ、何を読むにせよ手を動かしながら読むことをおすすめします。 慣れが一番大事です。
その他
-
Tensorflow, Pytorch: モデルのパラメータやレイヤーの数を弄って動かしてみること
- タスクはMNISTでも何でもいいので、動かしてみる。
- しかし、動かすだけではダメでした。色々弄ってまた動かし、結果を見たり、エラーに対処したりして初めて理解できることがほとんどなので、弄り倒しましょう。
3. MLへの薄っぺらい知識
この項目で語ることはあまり無く、とにかく機械学習手法の概観を正確にすることを目標にしました。
さすがに回帰やPCAといった基本的手法はマスターするよう努めましたが、その他の場合はできるだけ多くのMLモデルを把握することを最優先としています。
具体的に言うと、個々の解像度は「コードの1つ1つの意味が分かっていて(説明できて)、数式では30%ぐらい分かっている状態」を目標にしました。
このスキルセットで機械学習エンジニアとして何年もやっていけるか? と言われると間違いなくNOですが、新人エンジニアに重要なのは上司から降ってきたタスクを理解する言語を獲得することであって、モデルを組み立てることではありません。
そのため、フワッと理解しているものを出来るだけ多く作っておき、必要になったものだけ深掘りすることでこの部分の学習を先延ばしにし、他の部分の学習を優先しました。
ただ、怖いのが、深い理解を先延ばししても意外とモデルを組み立てたりのタスクがこなせちゃったりすることです。Tensorflowに甘やかされてますね。
参考書
上記に近い解像度で数理モデルを概観するのにぴったりの書籍が多モデル思考です。
多少分厚い本なので、興味のある章から徐々に読んでいくのがおすすめです。
また、個人的にめちゃくちゃおすすめなのはTensorFlow Core チュートリアルです。
PyTorchに親しんでいる場合は学習コストが発生するかもしれませんが、大体のMLモデルを(多少怪しめの)日本語で解説してくれていますし、全てJupyter Notebookでの実装付きです。
4. 職場での円滑なコミュニケーションに対する知見
===
- 基本的に新人は期待されている
- 何を期待されているかというと、現状の取り組みをキャッチアップして、安心して仕事を任せられる状態になることを期待されている
- 新人教育にはコストがかかる
- 新人の能力やキャパシティーを見極めるまでは今後の方針に不確実性が伴う
- 不確実性は新人に対する不安を惹起する
- 新規メンバーをチームに馴染ませていく対応中は全体の作業が遅れる
...というのは、今の業務を始める前に頭の中でなんとなく考えていた新人(自分)の客観的な立場を言語化したものです。
自分は学生なので社会のことは正直よく知りませんが、多分新人はこういう風に見られているものだろうと仮定し、そこから新人に求められることを考えて(と言ってもボロボロでしたが)行動しました。
参画後、実際に他メンバーからアドバイスいただいたことも含めると、新人の振る舞いとして最初に求められることは以下の3つがあると思っています。
- 自らを開示すること
- 質問・伝達は結論と要点を見やすくし、文脈と相手に起こしてほしいアクションを欠かさず書くこと
- 質問は自分だけでは解決できなかった理由を明らかにすること
自らを開示すること
理由としては、"チームが"新人に対して抱いている不安を解消するためです。
誰しも、できれば新人の方々に対して積極的にアプローチしたいと思っているはずですが、新しいメンバーが増えると他のチームメンバーは基本的にいつもより忙しくなるので、どうしてもその時間がとれない状況も出てきます。
となると皺寄せは多少なりとも新人に来るもので、チームが自分に対して抱える不安を積極的に解消しなければ、いつまでも他のメンバーから信頼を得られないことがあるかもしれません。
新人こそデカい不安を抱えている状況下で、場合によってはめちゃくちゃ難しいことかもしれませんが、頑張って自分から自らの情報を開示してみましょう。
新人を歓迎しないチームは絶対に健全ではないと思うので、頑張って自発的に開示したのに返事がない...という場合は職場がおかしいです。
質問・伝達は要点・結論を見やすくし、文脈と相手に起こしてほしいアクションを欠かさず書くこと
これは誰でも「まあそうだよね」となることだと思いますが、「なぜそうするべきか」の理由がちょっと異なります。
理由というのは2つあります。
-
相手の読解コストを下げるため
これは当然ですが、
-
自分が質問したい・伝達したいことを"自分が"整理するため
こちらの恩恵が意外にもかなりデカいです。
というのは、人に何かを伝えようとして文章を書いているとき、
- 要点・結論
- 簡潔に言うと自分は何を伝達したいのか・質問したいのか
- 文脈
- 要点にたどり着く経緯はどのようなものなのか
- 相手に起こしてほしいアクション
- 文脈と要点を読ませた上で、相手に何を求めているのか
の3つの要素がうまく書けない内容については、自分でも整理できていないことが浮き彫りにできるということです。
- 要点・結論が分からない -> 何を言いたいのかが実ははっきりしていない
- 文脈が分からない -> 何が目的なのか or 本当にその結論が正しいのか分かっていない
- 相手に起こしてほしいアクションが分からない -> 相手に伝える意味が分かっていない
というように自分の考えのうち、しっかりとしていない部分が見えてきます。
そして、この3つが抜け漏れなく揃ったコミュニケーションは大体相手に伝わりますので、ぜひ意識してみてください。
質問は自分だけでは解決できなかった理由を明らかにすること
これは読む側の読解コストを下げるという意味で先述の内容と少し被る部分もありますが、先述のものより感情面に寄ったTipsです。
先ほども申しましたが、他メンバーは新人の能力を把握しきれていないですし、不安に思っています。
そんなときに突然、
<シュココ! 質問です! このエラーはどうすればいいですか? (ここにエラー画面の画像)
<シュココ! 質問です! ここでもエラーが出ました! (画像)
<シュココ!
<シュココ!
:
と来たら、入ってすぐはそうだよね...、と思う一方で、少し嫌だと思いませんか? (自分はこれをやりました)
一方で、質問するまでに一生懸命取り組んだものの、やっぱり分からなかったという経緯を質問と一緒に載せることで、自分が何でも質問しているわけではないことが示せますし、自己解決能力の一端をチラ見せして他メンバーをホッとさせることもできます。
解決に至らなかった取り組みを参考にして問題の原因を狭めることもでき、質問を読む側にとっても有益なので長々と書いてしまいましょう。
.
.
.
作業環境を整えるためのこと
エンジニアを始めてから4ヶ月間、PCを触っては毎日のように作業効率化アプリを入れては消してを繰り返してきました。
その成果か、自分のPCの中で生き残っているものには結構良いアプリが揃ってきていると思っていて、共有する価値があると感じたので、ここで紹介していきます。
アプリケーション
Notion
Mac - 対応
Win - 対応
ToDo管理やWeb記事の保存など、割と全ての記録に使っています。
以前はEvernoteを使っていたんですが、動作の重さがネックで使いづらさを感じていただけに、Notionの軽快さには感動しました。
Notion APIでファイルをアップロードできるようになれば完璧なんですが、それはさすがにワガママが過ぎるかもしれません。
Alfred 4
Mac - 対応
Win - 標準機能でまあまあ実現可能
Mac使いなら一度は聞いたことがあるかもしれません。
無料版だと既存のSpotlightの拡張版的なアプリです。
他のアプリを呼び出す機能とか、計算機機能とかがついています。
ここまででもまあまあ便利ですが、Alfred 4には買い切りの有料版(結構高い) があり、これを買うとめちゃくちゃ便利にカスタマイズできます。
多機能すぎて語り尽くせないんですが、例えば、
コマンドをAlfredから実行したり、
任意のサイト内でのワード検索を一発で出したり、
カーソルの位置を指定してスニペットを作成したりとか、
とにかくいろんなことができます。おすすめ。
BetterSnapTool
Mac - 対応
Win - 標準機能
ウィンドウのレイアウト調整という、Windowsが当たり前にできることをMacにも出来るようにするものです。
個人的には、お気に入りのウィンドウレイアウトを保存しておけば、ショートカットキーでいつでも呼び出せるという機能が便利だと思っています(これはWindowsにはない?)。
PopClip
Mac - 対応
Win - 代替ソフトあり -> SnipDo
選択部分をコピペしたり、小文字にしたり、DeepL検索にかけたりがワンタッチでできるアプリ。
普通にめっちゃ便利です。
iTerm2
Mac - 対応
Win - 対応なし、ただ純正コマンドプロンプトより良いソフトあり -> Hyper
DLしたものをそのまま使うとただのターミナルと変わらないんですが、色々いじると複数画面を結合できたり、作業中の画面にオーバーレイする形で表示できたりするターミナルの上位互換です。
ただ、色々いじるのが結構手間なので頑張る必要があります。
自分は以下のQiita記事を見て設定しました。
Starshipとか入れると更にいいですが、そこら辺はお好みで。
Reeder 5
Mac - 対応
Win - 代替ソフトあり -> feedly
RSSリーダーです。iPhoneアプリもあるので併せて使っています。
iPhoneアプリの方は普通かな? という感じですが、Macだとショートカットキーがめちゃくちゃ充実してて嬉しいです。
Yoink
Mac - 対応
Win - 代替ソフトあり -> Fences®4
画面遷移のときにダルいドラッグ&ドロップのアレを解決するアプリです。
このアプリを発見したときは思わず拍手しそうになりました。
Chromiumアドオン
1Password
有名なパスワード管理アプリ・アドオンです。
今まではパスワードの管理方法を決定していなかったので、ブラウザにパスワードを記憶させるとか、自分で頑張って覚えるとかしていました。
すると頻繁に「パスワードなんだったっけ...」が発生していてめちゃくちゃ面倒だったんですが、パスワード管理ソフトに移行するのもダルいという板挟みになっていて最悪だったので、スマホの機種変更時に思い切って1Passwordに移行しました。
マスターパスワードさえ頑張って覚えれば、他サービスのパスワードは記号・英数字を含んだ64文字のパスワードとかに出来るのでおすすめです。
Notion Web Clipper
EvernoteみたいなウェブクリップがNotionでもできるようになるアドオンです。
Checker Plus for Gmail
メールクライアントを開かずにアドオンをクリックするだけでGmailが確認できるアドオンで、まあ便利です。
書いていると楽しくなって、結構紹介してしまいました。
おわりに
この4ヶ月間は素晴らしい先輩方にサポートいただき、とても実りある時間になりました。
しかし一方で、後回しにしていた数理的な知識の吸収とか、Python以外の言語(Rustとか)の学習とか、最近発生しつつあるマネジメント系の業務とか、まだまだ知らなければいけないことが山ほどあるので、もうしばらくは楽しい時間になりそうです。
ここまで読んで頂きありがとうございました。何かの参考になれば幸いです。