木走日記

場末の時事評論

ミドル・シニア世代のための『機械学習はじめの一歩の一歩のそのまた一歩』その2〜Pythonで放物線を描いてみる

私は長年工学系の学校で外来講師をさせていただいていますが、ここ何年か今はやりのAI・機械学習の講義を受け持たせていただいておりまして、その関係もあり、一般の人(特にミドル世代からシニア世代(40代〜60代以上))から、機械学習を学習したいのだがどうすればよろしいのか、といった質問をよくいただいたりします。

機械学習を学習したい』との意味を、その原理からすべて理解したいととるならば、線形代数、ベクトル、行列、微積分、偏微分などの数学知識が必須となります、もちろんプログラミングの知識も必要です、一般の人にはハードルがやや高いのです。
ですがやる気のある人には、私は『ぜひ機械学習を学習してください、もし習得できればあなたの将来は、人生は大きく開けます、その可能性を広げることでしょう』と励ましたいです。
そこまで大袈裟にしなくても、老後のボケ防止(苦笑)には機械学習習得は最適です。

そこで前回は、『機械学習はじめの一歩の一歩のそのまた一歩』と題しまして、読者のみなさまが機械学習の学習に踏み出す準備のエントリーをさせていただきました。

ミドル・シニア世代のための『機械学習はじめの一歩の一歩のそのまた一歩』
kibashiri.hatenablog.com

「このエントリーの内容が理解できれば、あなたはすでに『機械学習』を学習する準備ができていると私・木走が保証いたしましょう」と生意気な言い切りをしたエントリーでしたが、なぜか一部の読者から熱狂的な支持をいただきました。

そこで本エントリーは機械学習の学習第二段としまして、AIやディープラーニングで注目されているプログラミング言語Python(パイソンと発音します)、を読み物として一から学習いたします。

読み物としてエントリーを楽しんでください。

ただし中学数学や高校物理の教科書レベルの数式は登場しますのであしからずです。

もしこのエントリーが好評でしたら、『ミドル・シニア世代が一から学ぶ機械学習』と題してシリーズ化してもよいかもと考えています。

以下のメールアドレスにレスいただければ、励みになりますので幸いです。

木走正水のメールアドレス
mkibashiri@gmail.com

プログラムで「かめ」(turtle)モジュールを使用してアルゴリズムを学習します。

何はともかくPythonプログラムをご紹介。

from turtle import *
forward(0)
done()

このプログラムを解説するとこんな感じ。

f:id:kibashiri:20200302183007p:plain

一行目は、今から「かめ」で遊ぶぞ宣言です。
三行名は、プログラムがここで「おしまい」の意味です。
実質の命令は二行目だけです、「かめ」よ、まっすぐ前に0歩進め、というコマンドです。
0歩だから「かめ」は動きません。
実行結果はこんな感じ。

f:id:kibashiri:20200302183025p:plain

拡大するとこんな感じです、「かめ」はちっともかめじゃない、可愛くありませんw。

f:id:kibashiri:20200302183046p:plain

亀を200歩前進させ、その後進行方向左に90°顔をフラしましょう。

from turtle import *
forward(200)
left(90)
done()

こんな感じです。

f:id:kibashiri:20200302190828p:plain

200歩進んでは左向くを4回繰り返します。

from turtle import *
forward(200)
left(90)
forward(200)
left(90)
forward(200)
left(90)
forward(200)
left(90)
done()

正方形が描画されます。

f:id:kibashiri:20200302191232p:plain

いくつか応用。

from turtle import *
forward(200)
left(120)
forward(200)
left(120)
forward(200)
left(120)
done()

正三角形

f:id:kibashiri:20200302192344p:plain

では、星形。

f:id:kibashiri:20200302192613p:plain

このプログラムはこうなります。

from turtle import *
forward(200)
left(144)
forward(200)
left(144)
forward(200)
left(144)
forward(200)
left(144)
forward(200)
left(144)
done()

さらに応用を考えます。
五歩前進したら2°左を向きます。これを15回繰り返します。

from turtle import *
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
forward(5)
left(2)
done()

2°×15回、つまり30°の円弧が描かれます。

f:id:kibashiri:20200302194715p:plain

180回繰り返せば、円の出来上がりです。

from turtle import *
forward(5)
left(2)
・・・ 180回繰り返す
forward(5)
left(2)
done()

f:id:kibashiri:20200302195132p:plain

しかし、forward(5)とleft(2)を180回繰り返すのはかったるいですね。

そこでPythonでは繰り返しをするコマンドが用意されています。

from turtle import *
for i in range(180):
  forward(5)
  left(2)
done()

解説するとこうです。

f:id:kibashiri:20200302200910p:plain

さて「かめ」は自分の移動した軌跡を描いていきますが、軌跡を描かないで移動することもできます。
「かめ」をペンとすれば、up()コマンドでペンを上げ(軌跡を書かない)、down()コマンドでペンを下ろし(軌跡を書く)ます。

次のプログラムは直線を描くことと描かないことを20回繰り返します。

from turtle import *
import math
for i in range(20):
  forward(10)
  up()
  forward(10)
  down()
done()

つまり破線が描かれます。

f:id:kibashiri:20200306002138p:plain

では応用です、破線で円を描くにはどうしましょうか。
もう一度円を描画するプログラムを見てください。

from turtle import *
for i in range(180):
  forward(5)
  left(2)
done()

5歩進んで2°左を向くを180回繰り返していますね。
実は for i in range(180): の i は繰り返しのカウンタの役目を持っています。
0 から 179 に繰り返すたびに1づつ自動的にカウントアップしていきます。
そこでiが偶数のときペンを下ろし、奇数のときペンを上げるようにすれば、
破線で円が描けるはずです。
次のプログラムがそれを実現しています。

from turtle import *
for i in range(180):
  if(i%2==0):
    down()
  else:
    up()
  forward(5)
  left(2)
done()

if〜elseコマンドを覚えましょう。
もし条件が成立したらdown()を実行、そうでなければup()を実行します。
条件は(i%2==0)です、意味は(iを2で割った余りが0と等しい)つまり、iが偶数であることです。
説明するとこんな感じ。

f:id:kibashiri:20200306014306p:plain

実行すれば円が破線で描かれています。

f:id:kibashiri:20200303054707p:plain

さて、「かめ」を使ってグラフを描いていきましょう。

まずはx軸とy軸を描きましょう。

goto(x,y)コマンドを使います。

goto(x,y)コマンドは、「かめ」をその座標まで動かします。

x軸を(-300,0)から(300,0)、y軸を(0.-300)から(0,300)として描くとすると次のようになります。

from turtle import *
up()
goto(-300,0)
down()
goto(300,0)
up()
goto(0,-300)
down()
goto(0,300)
done()

up()とdown()を駆使していますね、結果はこんな感じです。

f:id:kibashiri:20200306160511p:plain

この座標空間はx軸方向で-300〜+300、y軸方向でも同じく-300〜+300の大きさであることを留意しておきます。

さてこの空間に、直線 y=2x + 100 を描きましょう。

ここで、y=300を代入すると、x=100ですね。(100,300)

次にy=-300を代入すると、x=-200です。(-200,-300)

この2点を結べば、直線 y=2x + 100が描けるはずです。

color(’色’)コマンドを使って直線は赤、color('red')としましょう。

from turtle import *
up()
goto(-300,0)
down()
goto(300,0)
up()
goto(0,-300)
down()
goto(0,300)
color('red')
up()
goto(-200,-300)
down()
goto(100,300)
done()

実行結果です。

f:id:kibashiri:20200306162207p:plain

さてプログラムも長くなってくると読みづらくなります。
コメント(注釈)を付けてあげましょう。
Pythonでは先頭に#を付ければ、そこに説明を付けられます、こんな感じ。

from turtle import *
#x軸描画
up()
goto(-300,0)
down()
goto(300,0)
#y軸描画
up()
goto(0,-300)
down()
goto(0,300)
#直線 y=2x + 100を描画
color('red')
up()
goto(-200,-300)
down()
goto(100,300)
done()

さてここからいよいよ放物線を描いていきましょう。

放物線は第一象限だけ使いますので、まずは原点座標を左下に移動して、x軸とy軸を描きます。

#初期座標
x0 = -450
y0 = -350
#x軸描画
up()
goto(x0-20,y0)
down()
goto(500,y0)
#y軸描画
up()
goto(x0,y0-20)
down()
goto(x0,400)
done()

実行結果です。第一象限だけ大きく確保できました。

f:id:kibashiri:20200319192550p:plain

さて放物線を描くには、三角関数を使用します。

点(x0,y0)から初速V0で角度θで物体を投げるとします。

重力加速度をgとすれば、t秒後の位置 ( x(t) , y(t) ) は次の式で表せます。

f:id:kibashiri:20200319193037p:plain

プログラムを説明しましょう。

f:id:kibashiri:20200322202230p:plain

まず2行目ですが数学系の関数を使うための宣言です。

その後、x軸とy軸を描画してますね。

ではプログラムの続きを解説しましょう。

f:id:kibashiri:20200322203715p:plain

重力加速度9.8を変数Gにセットします。

次に放射角度60°を変数DEGにセットします。

さらに初速度100を変数V0にセットしています。

このように重要な数値を変数にセットしておくと、プログラムのメンテナンス性が高まります。

例えば放射角度を45°に変更するのは変数DEGに45をセットすれば、プログラム全体が変更されるのです。

さて放射角度θのsinとcosを計算している2行は少し解説が必要です。

実はPythonで用意されている三角関数sinとcosは角度を度(60°とか)ではなくラジアンπ/3とか)で管理しています。

そこでmath.radians関数で変数DEGをラジアンの変換した上でmath.sinやmasth.cosの三角関数を呼び出しています。

次に時刻tを0秒にセットし、最後に初期座標に移動して準備完了です。

さて放物線を描いていきます。

f:id:kibashiri:20200322210312p:plain

時刻を1秒経過させ放物線を赤色で描いています。

実行結果です。

f:id:kibashiri:20200322210425p:plain

10秒後まで繰り返して描いてみましょう。

ここまでのプログラムをまとめておきます。

from turtle import *
import math
#初期座標
x0 = -450
y0 = -350
#x軸描画
up()
goto(x0-20,y0)
down()
goto(500,y0)
#y軸描画
up()
goto(x0,y0-20)
down()
goto(x0,400)
#重力加速度
G=9.8
#放射角度θ
DEG = 60
#初速度
v0=100
#放射角度θのsinとcos
sinDEG = math.sin(math.radians(DEG))
cosDEG = math.cos(math.radians(DEG))
#時刻初期値
t=0
#ペンを上げて初期座標に移動
up()
goto(x0,y0)
down()

color('red')

t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)
t = t + 1
x = x0 + v0 * cosDEG * t
y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
goto(x,y)

実行結果です。

f:id:kibashiri:20200322212330p:plain

10回繰り返し部分をfor文ではなくwhile文を用いて簡潔にしましょう。

f:id:kibashiri:20200322212905p:plain

while文は継続条件を指定してブロックを繰り返します。

while文を用いて20秒後まで描いてみましょう。

from turtle import *
import math
#初期座標
x0 = -450
y0 = -350
#x軸描画
up()
goto(x0-20,y0)
down()
goto(500,y0)
#y軸描画
up()
goto(x0,y0-20)
down()
goto(x0,400)
#重力加速度
G=9.8
#放射角度θ
DEG = 60
#初速度
v0=100
#放射角度θのsinとcos
sinDEG = math.sin(math.radians(DEG))
cosDEG = math.cos(math.radians(DEG))
#時刻初期値
t=0
#ペンを上げて初期座標に移動
up()
goto(x0,y0)
down()
color('red')
while ( t <= 20 ):
  t = t + 1
  x = x0 + v0 * cosDEG * t
  y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
  goto(x,y)

実行結果です。

f:id:kibashiri:20200322214829p:plain

おお、ついに放物線が描けました。

しかし右下に着目するとy<0の範囲まで落下していて、最後は描画枠からはみ出して終わっています。

これはかっこ悪いですね。

最後に無限ループとbreak文を学習いたしましょう、なかなか高度なテクニックです。

f:id:kibashiri:20200322221239p:plain

細かい説明は省きますが、放物線を最後x軸に交わるまで描画しています。
そのとき無限ループから脱出してプログラムは終了します。

実行結果です。

f:id:kibashiri:20200322223016p:plain

放物線が完成です、以下が最終プログラムです。

from turtle import *
import math
#初期座標
x0 = -450
y0 = -350
#x軸描画
up()
goto(x0-20,y0)
down()
goto(500,y0)
#y軸描画
up()
goto(x0,y0-20)
down()
goto(x0,400)
#重力加速度
G=9.8
#放射角度θ
DEG = 60
#初速度
v0=100
#放射角度θのsinとcos
sinDEG = math.sin(math.radians(DEG))
cosDEG = math.cos(math.radians(DEG))
#時刻初期値
t=0
#ペンを上げて初期座標に移動
up()
goto(x0,y0)
down()
color('red')
while 1:
  t = t + 1
  x = x0 + v0 * cosDEG * t
  y = y0 + v0 * sinDEG * t - 0.5 * G * t * t
  if(y < y0):
    ye = y0
    te = v0 * sinDEG * 2 / G
    xe = x0 + v0 * cosDEG * te
    goto(xe,ye)
    break;
  goto(x,y)

いかがでしたでしょうか。

これがPythonのプログラムです。

多くのPythonの学習書はPCにPythonの開発環境をインストールするところから説明が始まります。

そしてキーボードでプログラムを自ら打ち込み、プログラムを実行することで学習を進めるように構成されています。

私はミドル・シニアのプログラミング学習方法はこの方法ではダメだと見切っています。

初めに開発環境をインストールでもしようものなら、多くの真面目な読者は妙に構えてしまい、習得しなければならないと力んでしまうのです。

最初は、軽い読み物として学習すればよろしいのです。

そのうえで関心が持てればインストールをすればよろしいのです。

多くのテキストではPythonは文法が優しい、初心者でも覚えやすいと記述されています。

これも「真っ赤な嘘」です、ミドル・シニア世代の錆び付いた(失礼)頭脳のサビを落とすのには、Pythonの学習は最適ですが、決して優しくはないのです。

まずは、読み物としてこのエントリーを理解してください、すべてはそこからです。

このエントリーの内容が理解できれば、あなたはすでに『機械学習』を学習する準備ができていると、長年『機械学習』を学生に教育指導してきた私・木走が保証いたしましょう。

いかがでしたでしょうか。



(木走まさみず)