どうも、カタミチです。
さて、今日も「最短コースでわかる PyTorch&深層学習プログラミング」のひとり読書会、やっていきますかねー。
今日は、1-5節ですね。張り切っていってみよー
1-5. カスタムクラス定義
今回の話は、クラス、インスタンスですね。これまでもチョコチョコ出てきていますが、ここでしっかり理解しておきますかねー。
クラスは「型」でインスタンスはこの「型」から生成された個別の実体。
と説明がありました。一応、私はこの辺はそんなに違和感ないのでサラッと進むことにします。ちなみにクラス内の変数の値は「属性」と呼ばれる…と。
具体的に見ていきますかねー。
「matplotlib.patches」ってのが初登場ですね。グラフの中に円や長方形などの図形を書くのに便利なライブラリみたいです。
で、ライブラリのimportのあとはクラス(class)の定義。初期化関数「__init__」で属性「x, y」を引数から持ってきて定義していますね。あと、drawって関数も定義されています。グラフ上に点を打つ関数みたいですね。pltのplot関数の中で、まとめて描画の仕方が書かれています。
classの概念&書き方は初見殺しなので、しっかり理解しておきたいですね。特に「self」って何やねん!というのがあると思いますが、ひとまず「そんなもん」と思っておいたほうが精神衛生上良さそうです。
さて、クラスの定義が終わったところで実体化。インスタンスを作ってみよう〜、というところですね。
あら?Pointの引数は「self」と「x」と「y」の3つじゃ無かったっけ?…というのがお約束の質問ですね。結論から言うと、「self」はとりあえず無視しろ!(雑)ってことになるようです。私も気になって少しググってみたりもしましたが、深淵を覗き込むような雰囲気があって、逆に深淵から覗き込まれそうだったのでやめておきました(ニーチェ並感)。
ともあれ、インスタンスが2つできました。「p1」と「p2」ですね。一応中身も確認してみて、それぞれのインスタンスの属性x、yにインスタンスごとの値が入っていることが見て取れます。
では、あらかじめ作っておいたdraw関数で、p1とp2の点をグラフ上に描いてみましょう。
どん!
plt.xlim、plt.ylimは、グラフの表示範囲ですね。
\(-4 \leq x \leq 4\)
\(-4 \leq y \leq 4\)
ってことですね。
その下のplt.gridは、グラフの中の格子状のうっすらした線を出す関数ですね。本書のサンプルにはこいつが書かれてなくてもうっすら線は出てたんですが、私の環境だと真っ白背景になってしまったので、とりあえず勝手に追加しました。
それにしても、グラフ領域がなんだか横長になってるな…
さて、続いて「クラスの継承」ですね。本書の中では3段階で分かりやすく説明がされていますが、ここでは3段階目のコードだけ書いておきますかね。(ちなみに、実際にはちゃんと3段階とも手で書いて勉強しました、はい)
見てみましょう…
どん!
上で定義したPointクラスを継承した「Circle3クラス」を定義しています。
初期化関数のところでは、super().__init__ってのが呼び出されていますが、この「super().」ってやつが「継承元の〜」という意味ですね。つまり、継承元と同じくxとyを属性にセットしている事になります。構造の観点で言うと、継承先のクラスでは、継承元の関数が呼び出せるってことになりますね。4年3組だった私は5年生になってクラス替えがあっても、4年3組だった頃の友達をクラスに呼び出せるってことですね(ぜんぜん違います)。
…あとは、継承元では定義されていない「r」を新たに属性として定義している…って感じですね。ちなみに、super()の行を「self.x = x」「self.y = y」って書いても最終結果は同じになりますが、継承している意味ないやん、みたいな空気になりますかね。
次に、継承元でも定義されていたdraw関数です。何も書かなければ継承元のdraw関数が有効なようですが、継承先で改めて書くことで内容を書き換えることができる…ってことになってます。オーバーライドってやつですね。しかし、オーバーライドする関数内で、元の関数自体を呼ぶ(super().draw())ってこともできるんですね。ふむふむ。
新しくなったdraw関数は、グラフ上に(x, y)点を打ちつつ、その点を中心とした半径rの円を描く関数としてパワーアップしましたね。patchesはこういう使い方&書き方をするみたいです。最終行(ax.add_patch(c))は円グラフを描画するための処理みたいなんですが、axって変数はどこにも定義されていないぞ…?
さて、Pointクラスを継承したCircle3クラスを使って円グラフを書いてみましょう。せっかくなので、上でPointクラスをインスタンス化したp1とp2にも登場してもらいます。
どん!
1行目でインスタンス化は完了で、後の行は図形描画用の処理ですね。
この描画処理の中で、上に出てきた「ax」が定義されています。「ax = plt.subplot()」ですね。subplotってやつは確か、グラフを複数並べたい時に使うやつだったと思ったんですが、patchesで円グラフ書くときにも使うんですね。レイヤーを重ねるイメージなんだろうか…。
それにしてもこの「ax」、完全にCircle3クラスの外で変数定義されてますが、グローバル変数的な扱いなんだろうか…。外でaxを定義する前提でclassの中に仕込んでおく、って書き方に見えますが、こういう書き方できるんですね(ふむ)。
ちなみに、グラフが横長になるのを防ぐために、「ax.set_aspect('equal')」ってのを書いてみました。本書内では、こんなの書かなくても横長じゃない雰囲気でしたが、とりあえずうちの環境だと横長になってしまうので無理やり書いてみました。しかしこれ、なぜか「plt.」じゃなくて「ax.」なんですよね…。なぜなんだー。まぁ、きっといつかわかる日がくるか(遠い目)。
ともあれ、Circle3クラスの活躍によって、(1, 0)を中心とした半径2の地球っぽい円が書けましたね。Circle3の「3」は、太陽系第3惑星の3だったんですね(違います)。ついでに月っぽいp1、p2も書けました(月が2つ…?)。
ということで
コードだけ見ると「__init__なんじゃこりゃ〜」って感じもありますが、とりあえずはまだ、ついて行けてる感じですかね。まぁ、まだ1章なのでここで脱落したらどうしようもないんですが…。
ともあれ、コード書いていじってみるのは、やっぱり楽しいですね。引き続き頑張っていきたいと思います(^ ^)/
ではまた。