プログラミングに対する劣等感
今まで、メルプや他のサービス、医師業を通じて、臨床・起業・営業・CS・事業戦略・マーケティング(web・オフライン)・動画作成・PR・交渉・開発(ソフトウェア・ハードウェア)・デザイン・採用・経理など、一通り様々な経験をしてきた。
その中で、自分が一番苦手意識があり、他と比べて圧倒的に難しいと感じたのが、開発(プログラミング)である。
大学5年生くらいからプログラミングを始め、医師×エンジニアという括りで、プログラミングを好きでやっていたが、どうにも他と比べて理解が進まないという感じがしていたし、周囲からの評価に対して自分の実力が追いついていないのではという気がしていた。
営業やCS、webマーケなど、各々の部門もそれぞれの難しさはあるものの、扱う概念の抽象度という観点では、プログラミングが一番難しいと感じていて、それゆえ自分の理解力が追いついていないのではと。
デザインのように、パッと目に見えるものでもないし、人間が話す言語でやり取りするもの(臨床・営業・CSなど)でもなく、プログラミング言語という極めて抽象的でロジカルな概念を取り扱うという点で難しい。なので、個人的には、あらゆる職種の中でエンジニアの人達を一番尊敬している。
もしかしたら、最先端の数学や物理などは、さらに抽象度が高く難しいかもしれないが。
職種としてプログラミングのみをやっているエンジニアと比べて、自分はプログラミングのみに割けられる時間が少ないから、忘れてしまうし仕方ないのでは?と自分で言い訳をしていた時期もあったが、同時に、単純に自分が頭が悪くて理解力がないだけなのでは?という思いも抱いていた。
自分がうまくプログラミングできなかったコードを、エンジニアに質問してすぐに回答をもらえた時などは、「なんで、自分はできないんだろう?」と思ったりした。
そんな自分の苦手意識やプログラミングに対する劣等感を払拭したいと思い、薄々気づいてはいたものの、「しっかり理解することを怠らない」というのを今年秋頃から実践し始めた。
理解する努力を怠らないというのはどういうことか?
理解する努力を怠らないというプロセスは、次の3段階からなる。
「必ず一次情報からアプローチし、分からなければ単語レベルにまで遡って分解して理解に努め、完全に理解し切るまで努力することを諦めない」ということである。
プログラミングの例になるが、たとえば、OpenAIのAPIを使って、ChatGPTのように、ユーザーからのテキスト入力の質問に対して、回答できるようなサービスを作りたいとする。
必ず一次情報からアプローチする
この場合、まずやることは、OpenAIのAPIのドキュメントを読むことである。
QiitaやZennなどで、他のエンジニアの方々がまとめてくれた、一見分かりやすい記事(実際分かりやすいこともある)、自分の今回やりたい機能に関してすでにフォーカスしてくれている記事に飛びつきたくなるが、一次情報をしっかり読んだ方が結果的には早い。
APIの更新がすでに行われていて、公式ドキュメントでは最新バージョンが記載されているのに、まとめ記事では最新バージョンの記載にはなっていなくて、そのままコードをコピペしたけどなぜか動かなくて延々と悩むとか、そもそも情報が間違っていて振り回されるという可能性がある。
ちなみに、OpenAIのAPIのドキュメントはこんな感じ。
この時点で、「うわぁ、英語だ」となってしまうが、我慢して全部読む。できれば、どこかで、ドキュメントを一読する時間を取れると尚良い。
分からなければ、単語レベルで調べる
前提知識の差によって、APIのドキュメントを1回読んですんなり全部分かる人もいるだろうし、なぜ、このコマンドでテキストの中身が取得できるの?と疑問に思う人もいるだろう。
もし疑問が湧いてくれば、そこでうやむやにせず(このメソッドで呼び出せるからとりあえずコピペすれば良いかではなく)、単語レベルでどんどん調べていく必要がある。
幸い便利なことにChatGPTが誕生したので、下記のように「中学生にもわかるように」とか「初心者にもわかるように」と枕詞を添えて聞いてみる。
OpenAIのChat Completionsの呼び出し部分ですが、どうして、このコマンドでテキスト情報が取得できるのですか?
中学生にもわかるように説明してください
console.log(completion.choices[0].message.content);
こんな感じで返答が来る
もちろん、中学生にもわかるように説明しますね。
`console.log(completion.choices[0].message.content);` というコマンドは、プログラミングの世界で「あるデータを取り出して、それを画面に表示する」という意味です。このコマンドは特に、JavaScriptというプログラミング言語で使われるものです。
このコマンドを理解するために、それぞれの部分を簡単に説明します。
1. **`console.log(...)`**:
これは「コンソールにログを出力する」という意味です。コンソールは、プログラムの実行結果やメッセージを表示する画面のことです。`console.log`は、括弧の中にあるデータやメッセージをコンソールに表示するために使われます。
2. **`completion`**:
これは「完了」という意味ですが、ここでは特定のタスクや処理が終わった後の結果を含むデータのことを指しています。たとえば、これは「OpenAIのChat Completions(チャット完了)」という処理の結果かもしれません。
3. **`completion.choices[0]`**:
これは、「completionの中のchoicesというリストから最初の要素を取り出す」という意味です。コンピューターでは、リストの最初の要素は「0」という数字で表されます(これを「インデックス」と言います)。
4. **`completion.choices[0].message`**:
これは「最初の選択肢の中のメッセージ」という意味です。つまり、リストの最初の要素(ここではチャットの返答など)の中から、メッセージという部分を取り出すということです。
5. **`completion.choices[0].message.content`**:
これは、「そのメッセージの中身」を表しています。つまり、チャットの返答などの具体的なテキストがここに含まれているというわけです。
簡単に言うと、このコマンドは「OpenAIのチャットの結果から、最初の返答の具体的な内容を取り出して、それを画面に表示する」という動作をします。
踏ん張る
上記の説明でなんとなくわかったような気になるが、たとえば、さらに下記のように質問してみる。
completionsとはどういう形式の情報が入っていて、なぜドットで中身にアクセスができるのですか?
具体的な日本語の返答例を添えて説明してください
そうすると、下記のようにさらに分かりやすく、返答がくる。
わかりました。`completions` に関する説明を、具体的な日本語の返答例を用いて行います。
想像してみてください。あなたがOpenAIのAPIに「今日の天気は?」という質問を送ったとします。APIはその質問に対していくつかの異なる返答を生成し、それらを `completions` オブジェクトとして返します。
### completionsの形式の例
`completions` オブジェクトは以下のような形をしているかもしれません:
```json
{
"choices": [
{"message": {"content": "今日の天気は晴れです。"}},
{"message": {"content": "今日は曇り時々晴れでしょう。"}}
]
}
```
この例では、`completions` オブジェクトに `choices` という名前の配列が含まれています。そして、その配列の中には2つの異なる「選択肢」があります。それぞれの選択肢には、天気に関する異なる返答が含まれています。
### ドット記法でのアクセスの例
`console.log(completion.choices[0].message.content);` このコマンドは、`completions` オブジェクトの中の最初の選択肢に含まれるメッセージの内容を取り出して、画面に表示します。この例では、結果は以下のようになります:
```
今日の天気は晴れです。
```
このコマンドは以下のステップで動作します:
1. `completion.choices` で `choices` 配列にアクセスします。
2. `choices[0]` で配列の最初の要素(最初の返答)にアクセスします。
3. `message` でその返答のメッセージ部分にアクセスします。
4. 最後に `content` でメッセージの内容("今日の天気は晴れです。")を取得します。
このようにして、プログラムはOpenAIのAPIから返された複数の選択肢の中から特定の情報を取り出し、それを利用することができるのです。
こうした質問を繰り返して、完全に自分が理解したという状態になるまで踏ん張る。
時には、1つの機能を作成するのに、前提知識の理解だけで10時間くらいかかることもあるが(自分の場合はそんな時もあった)、ここで途中で「もういいや、やーめた」となってはダメである。
若干、こちらの記事で触れた、思考体力にもつながる。
「理解した」の基準をどこに置くか?
これは、他人に話せるまで、もしくは、記事としてまとめて公開できるまでを自分の基準にしている。
自分の頭の中で理解した状態では、「一応、理解できた気がする」状態であり、細部までしっかり理解はしていないことがほとんど。
正確には、情報としては頭に入ったが、それらが系統だって整理はされていないという状態。その状態でも、脳は「書いてあることは、わかったので理解できた」と思ってしまう。しかし、実際には自分で、その知識を使いこなせるレベルにまで昇華させないといけない。
なので、訓練として、周りに説明できるまで、もし説明する環境になければ、他の人が見ても分かるような記事としてまとめるのを行う。
理解に時間をかけすぎるに越したことはない。最終的に全体的な生産性は上がる。
理解することを途中でやめて次に行く癖がつくと、次同じような問題に遭遇した時も、また調べる必要が出てくる。結局その時もしっかり理解しないまま、なんとなくの理解で終えてしまうので、いつまで経っても身につかない。浅瀬で、ずっとシュノーケリングをやっているようなものである。
一方で、しっかり理解するということは、その原理原則まで立ち返るということである。たとえば、先ほどのAPIの例で言えば、
- そもそもデータを取り扱うJSON形式とはどういう形式なのか?
- 他にはどのようなデータ形式があって、なぜJSONが選ばれているのか?
- そもそもAPIとはどういう概念で、どのような仕組みで他のサービスの情報を取得できるようになっているのか?
などと、遡ろうと思えばどんどん遡れるし、さらに派生して、
- APIのBearer Tokenはどういう仕組みでどのように認証させているか?
といった関連する疑問も生じてきたら、調べててもキリがなくなるかもしれない。
それでも、5時間でも10時間でもかけてでも、原理原則まで立ち返って、理解に努めることで、次に似たような問題に遭遇した時に、すでに理解ができているので、すぐに実装することができ、最終的には全体的な生産性は上がる。かつ、一つの問題から紐づく感じで関連知識や原理原則を調べているので、知識が有機的につながるという利点がある。
最初は辛い作業だが、一回そこを乗り切れば、次回以降は楽になる。先ほどの例の対比で言えば、一度、ダイビングでできるだけ深く潜れば、次に出くわす似たような問題も、すぐに対応できるようになる。
目の前のやらなければならないタスクや仕事がたくさんあると、ついつい、わかった気になった状態で終わらせてしまうことが多いが、そこは我慢して踏ん張って理解することに努めることが重要である。
理解することを怠らないように努めた結果
というわけで、ChatGPTをメンターとして駆使しながら、「理解をすることを怠らない」ことに今年秋から努めた。
理解していた気になっていた前提知識の段階で、やはり理解できていなかった部分に気づき、1つの理解で5時間や10時間かかってしまうこともあり、今日何も仕事進んでないなという無力感に苛まれることも若干あったが、それを乗り越えた後では、物事を使いこなしている感覚がようやくプログラミングでも生まれてきた。結果的に生産性も上がった。少しずつでもしっかり理解に近付いている、という過程を楽しむことが重要である。結局は知的好奇心に帰着する。
あとは、ある程度の複雑で抽象的な事象でも、できる限り分解することで、理解することはできると感じた。とはいえ、プログラミングは最終的に数学に行き着くので、数学の抽象性を理解できるようになれるかは(そこまで踏み込むか、そもそも必要なのかは)未知数である。
リポバッテリーからの駆動をできる電子回路を書く場合にも、最初はPMOSFETって何だ?って感じで、とりあえずPMOSFETを使っている似たような回路をコピペしてなんとなく動かすようにできれば良いかなという邪念もよぎったが、
それではダメだと思い、遡りまくって半導体の基礎から勉強し直して、まとめたら、ようやく分かるようになって、結局その後の生産性も上がった。
自分の頭が悪いのではと思うこともあったが、理解が早い遅いは、基本的には前提知識の差だと思う(もちろん、1を聞いて10を知るという人もいるとは思うが)。
牛尾さんのこちらのnoteの記事にも、下記のように記載されていて、自分は救われた思いがした。
https://note.com/simplearchitect/n/n388201603a28
「いやぁ、ビデオ見ても難しいので、10回観てますわ。何回も見直して、分からないところポーズしてメモして観てます。」そうしたら、もう一人の同僚も「やっぱそうなるよね。自分も何回も何回も見たわ」。この出来る人2人の同じコメントには雷を打たれた気分になった。自分は難しいから1回観ても理解できへんし、そんなもんと思っているので、1回みてあんまり頭に入ってないけど、実行したり、デバッグし始めて、ちょっとづつ理解していくというステップを普段はとりがちだ。私は頭がいい人はああいうのを見て1発で理解できてうらやましいなぁと思っていたけどそうではないのだ。どんなに頭がいい人でも理解には時間がかかるものなのだ。頭のいい人が理解が早いように見えるのは、そうやって時間をかけてきて積み重ねているので、既に理解していることに関して頭のメモリにコンテキストが載っているからだ。
今回は、あくまでプログラミングを例に挙げたが、これはどの分野でも同じであり、論文を読むにしても専門用語で単語レベルで分からなければ、全部調べる方が結果的には早いし、中学受験・大学受験の問題も同じである。今振り返ってみると、大学受験の勉強の時も、「理解して勉強していた」とは言えなかったなと思わされた。
今後も、理解することを怠らずに、続けていこうと思う。
コメント