機械学習を学んでいて、
こんなふうに感じたことはありませんか。
- 勾配降下、最適化、収束……言葉は知っている
- 数式や図も、一応は見た
- でも
「結局、何が起きているのか」は、よく分からない
それは、あなたの理解力の問題ではありません。
見せ方の問題だっただけです。
本記事では、
「ランダムに配置された点が、少しずつ整列していく」
ただそれだけの動きを、Pythonで可視化しました。
実はこの動きこそが、
機械学習がやっていることの“正体”です。
今回やること(この記事で体験できること)
本記事では、次のような様子を可視化します。
- 最初:点は完全にランダム
- 途中:少しずつ、ある方向に引き寄せられる
- 最後:きれいに一直線に整列する
数式や専門用語を追う前に、
まずは 「学習している動き」そのもの を見てください。
使用環境・前提
使用するもの
- Python(Pythonista3 で動作確認)
- 標準ライブラリのみ
使用しないもの
- 機械学習ライブラリ(scikit-learn 等)
- 複雑な数式
機械学習の“核”だけを、
できるだけ裸の形で見せるためです。
まずはコードを動かしてみる
細かい説明は後回しで構いません。
まずは、動きを見てください。
from scene import *
import random
class AlignScene(Scene):
def setup(self):
self.n = 80
self.points = []
self.speed = 0.05 # 学習率(収束スピード)
for _ in range(self.n):
x = random.uniform(0, self.size.w)
y = random.uniform(0, self.size.h)
self.points.append([x, y])
self.target_y = self.size.h / 2
def update(self):
for p in self.points:
p[1] += (self.target_y - p[1]) * self.speed
def draw(self):
background(0, 0, 0)
fill(1, 1, 1)
for x, y in self.points:
ellipse(x - 3, y - 3, 6, 6)
stroke(0.3, 0.3, 0.3)
stroke_weight(1)
line(0, self.target_y, self.size.w, self.target_y)
run(AlignScene())何が起きているのか(超ざっくり)
この動きの本質は、次の たった1行 です。
p[1] += (target_y - p[1]) * speedやっていることは、とても単純です。
- 今の位置と、目標との差を計算する
- その差の「一部」だけ動かす
- それを何度も繰り返す
一気に正解に行かない。
これが、機械学習の基本姿勢です。
機械学習と、どうつながっているのか
この可視化と、機械学習の対応関係を整理すると、こうなります。
| 今回の可視化 | 機械学習 |
|---|---|
| 点 | パラメータ |
| ランダム配置 | 重みの初期化 |
| 中央の線 | 正解(教師信号) |
| 差分 | 誤差 |
| 少しずつ動く | 勾配降下 |
| 整列 | 収束 |
機械学習は、
賢く考えているわけではありません。
誤差を、少しずつ減らしているだけ
まずは、この感覚を持てれば十分です。
なぜ「y方向だけ」動かしているのか
今回は、あえて動きを 1方向だけ に限定しています。
- 見るべき変化を1つに絞る
- 観察ポイントをブレさせない
その結果、
- 「何が起きているか」が迷子にならない
- 収束という現象だけに集中できる
構成になっています。
発展:xも動かすと、どうなる?
実は、x方向も同じように動かすと、
これはそのまま 回帰分析(最小二乗法) になります。
- 線形回帰
- 多次元最適化
ただし本記事では、
理解体験を優先し、詳細は扱いません。
この可視化が役に立つ場面
- 機械学習を学び始めたとき
- G検定・DS検定の概念整理
- 「学習率」や「最適化」を説明するとき
- 自動売買・EAロジックの考え方を整理したいとき
数式の前に、
一度「動き」を見ておくだけで、
理解のハードルは大きく下がります。
まとめ
- 機械学習の本質は「誤差を減らすこと」
- 収束とは「状態」ではなく「過程」
- 分からなかったのは、能力の問題ではない
見せ方を変えるだけで、理解は前に進みます。
おまけ:コードの紹介
① 前提:このスクリプトの正体
このコードは
「機械学習“っぽい”ことをしているが、機械学習ライブラリは一切使っていない」
という点が重要です。
やっていることは厳密には:
最適化(Optimization)の可視化
です。
- 学習データ ❌
- モデル ❌
- 損失関数 ✅
- 勾配降下 ✅
👉 MLの核だけを、裸で動かしている
② 全体構造(俯瞰)
AlignScene(Sceneクラス)
├ setup() : 初期化(ランダム配置)
├ update() : 毎フレームの更新(収束ロジック)
└ draw() : 描画(見える化)
Pythonista の scene は
「60fpsで update → draw を繰り返す状態機械」 です。
③ import 部分
from scene import *
import randomscene
- Pythonista専用
- 高速アニメーション用フレームワーク
Sceneを継承して使う
random
- 初期状態を「完全ランダム」にするため
- 学習の初期値=ランダム初期化 と同義
④ クラス定義
class AlignScene(Scene):Sceneを継承- iOS画面全体が「学習空間」になる
⑤ setup():初期化フェーズ
def setup(self):👉 学習前の状態を作る
⑤-1 点の数
self.n = 80- 同時に動く点の数
- MLで言えば:
- サンプル数
- パラメータ数
- エージェント数
多すぎると iPhone が悲鳴をあげる
80 は安定値(実測)
⑤-2 点群の入れ物
self.points = []- 各点は
[x, y] - 状態ベクトルそのもの
⑤-3 収束スピード(超重要)
self.speed = 0.05数学的意味
これは 学習率(learning rate) です。
- 0.01 → 遅いが安定
- 0.1 以上 → 発散しやすい
※ 内部的に再計算すると0<α<1
を満たしており、必ず収束します。
⑤-4 ランダム初期化
for _ in range(self.n):
x = random.uniform(0, self.size.w)
y = random.uniform(0, self.size.h)
self.points.append([x, y])ここで起きていること
- 画面全体に一様分布で点を配置
- 偏りなし
- 事前知識ゼロ
👉 MLの「ランダム初期重み」と完全一致
⑤-5 目標ライン
self.target_y = self.size.h / 2- y方向の目的値
- 損失が最小になる状態
ここが 「正解」 です。
⑥ update():学習ステップ
def update(self):- 1フレーム = 1ステップ
- 60fps → 秒間60回学習
⑥-1 本質の1行
p[1] += (self.target_y - p[1]) * self.speed分解すると
| 部分 | 意味 |
|---|---|
self.target_y - p[1] | 誤差(error) |
* self.speed | 学習率 |
+= | 更新 |
数式で書くと
yt+1=yt+α(ytarget−yt)
これは:
- 勾配降下
- 誤差最小化
- 指数収束
全部成立しています。
⑦ draw():可視化
def draw(self):⑦-1 背景
background(0, 0, 0)- 黒背景
- コントラスト重視
⑦-2 点の描画
fill(1, 1, 1)
for x, y in self.points:
ellipse(x - 3, y - 3, 6, 6)- 半径3pxの白点
- 視認性最優先
⑦-3 目標ライン(教師信号)
stroke(0.3, 0.3, 0.3)
stroke_weight(1)
line(0, self.target_y, self.size.w, self.target_y)- 薄いグレー
- 「正解はここだよ」と示すだけ
- 点はこの線を直接見ていない ←重要
⑧ 実行
run(AlignScene())- Scene を起動
- 無限ループ開始
⑨ これを ML に対応させると
| このコード | 機械学習 |
|---|---|
| 点 | パラメータ |
| ランダム初期化 | 重み初期化 |
| target_y | 正解ラベル |
| 誤差 | 損失 |
| speed | 学習率 |
| update | 勾配降下 |
| 整列 | 収束 |
👉 MLの骨格を再現
