コンテンツにスキップ

EEIC前期実験i2 第1日

本日のメニュー

  • 実験全体の概要について
  • Google Colaboratory環境の利用について
  • Numpyを用いた数値計算事始め -- 機械学習を始める前に --
  • 機械学習とは

はじめに

本実験では機械学習の数理的基礎を学びながら、プログラムを動かすことで実際の実装に触れていく。最終レポートでは機械学習を用いた何らかの課題を自ら設定し、コードとともに結果をまとめることを目標とする。 本実験において用いるプログラミング言語はPython、実行環境はブラウザ上で対話的実行が容易なGoogle Colaboratoryである。

「Pythonを用いた」で始まる書籍は、昨今枚挙にいとまがない。特に機械学習の入門書と銘打たれている書籍では、多くの場合Pythonを実装言語として選択している。Stack Overflowのランキング では全ての回答者の総合でウェブ系言語(JavaScript, HTML/CSS)とデータベース言語であるSQL についで4位、AIを用いる学習者の部門では1位である。さあ流行りにのろう、という姿勢はエンジニアとしては向学心があり素晴らしいことであるが、一旦しっかり状況を整理しておくことは肝心である。なぜ機械学習にPythonなのか。

Pythonはインタプリタ言語であり、2年生冬学期で多くの電気系学生が学んだと思われるC、Rust、Pythonを比べた時、同一結果を得るプログラムを実行する速度は最も遅い。一方、機械学習の基盤となるのは数学、それを実装する場合プログラムによる数値計算が重要となり、この部分については基本的に速い方が望ましい。それでも機械学習のための言語としてPythonが選ばれる現状は以下の点に理由がある。

  • アクティブな開発者を背景とする豊富なライブラリ・リソース
  • インタプリタ言語であることによる高速な開発サイクル
  • C言語を核とする数値計算ライブラリ(Numpy)の存在

1点目は、特にアプリケーションの構成等を考えた場合に非常に重要な点である。巨人の肩の上に立つというやつである。2点目も見逃せない点である。完成されたプログラムの実行速度と開発速度のバランスは重要である。巨大なプロジェクトをコンパイラ言語で構成した場合、コンパイルだけで多大な時間を要する場合があり、新規機能の追加が早いなど高速な開発サイクルが必要となる場合には必ずしも適切ではない。3点目はPython言語そのものの弱点をうまく補完する部分と言える。Pythonなどのインタプリタ言語はC言語などでモジュールを書き、これをラッパーして利用する機能を有していることが多い。もしPythonにおいて数値計算をPythonの機能だけでスクラッチで書いてしまった場合、実用に耐える速度とはならない。NumpyはPythonにおける数値計算・科学技術プログラミングのためのライブラリであり、コアの部分はC言語で書かれている。インタプリタ言語による書きやすさと計算速度の両立が図られているのである。これらの要因が複合して、現在の機械学習におけるPythonの立ち位置がある。

「機械学習をやる」といった場合に、単に使うだけであれば該当するライブラリをインストールして、各種生成AIに使い方をお尋ねしながら知らない間にできあがり.... という状況も増えてきた。しかし仕組みや理論をしっかり理解することはやはり重要である。プログラミングにおいてもライブラリを利用し車輪の再発明を避けることは一つの美徳と言われているが、一方で基本的なアルゴリズム・理論を自らの手で実装したことがないのは「情報」と名のつく学問を修める上でいかがなものか。登山が好きな方からはお叱りの言葉を受けそうだが、「富士に一度も登らぬ馬鹿、二度登る馬鹿」という表現がある。これはプログラミングの世界にも通じる。

本実験は深層学習以前の、時に古典的と言われるようになった機械学習の基礎(線形回帰・線形分類、クラスタリングなど)を追いかけながら、Numpyなどを使って実装していく。機械学習の結果を得るだけなら、scikit-learnなど著名なライブラリが存在し、その使い方の習得という流れになるが、あくまでも理論と実装をしっかりと結びつけていく架け橋となるのが本実験である。

我々はAIを「使っている」のか?

「AIを使って〇〇を完成させました」「AIにXXをさせました」という文言をよく聞くようになったが、このAIを使役するような立ち位置は果たして妥当なのだろうか。もはや知的作業においても現在の生成AIの出力結果は、専門家に比肩もしくはそれより遥かに高度なレベルに達している。このレベルの「知性」に触れ続けていると、そのうち圧倒的に怠惰な方に言ってしまいがちなのが人間の性である。3の倍数でもないのに、AIが賢くなるのと同じオーダーのスピードで人類の方がアホになっているのではないかという懸念がある。さて未来はどうなることやら。封神演義という漫画で有名な藤崎竜さんの短編集「WORLDS」に収録されている「TIGHTROPE」に示唆深い話が描かれているので、興味がある人はぜひご一読を。

Google Colaboratory の利用方法

Google Colaboratoryとは

さて、本実験ではGoogle Colaboratoryを利用する。Google Colaboratory(以下 Colab)は、Googleが提供するクラウド型のPython実行環境である。 利用者は自身のPCにPythonやライブラリをインストールすることなく、Webブラウザ上でPythonプログラムを実行できる。 ColabはJupyter Notebookと互換性を持ち、データ分析や機械学習の実験環境として広く利用されている。以下にColabの利点を整理する。

  • Python環境の構築が不要(Numpyは標準利用可能)
  • Webブラウザのみで利用可能
  • Google Driveと連携できる
  • GPUも利用できる(規模・時間の制限あり)
  • ノートブック形式で実験記録を残せる

最後の部分も重要で、本実験のレポートはノートブック形式の中に文章を記載した上で、このノートブックを提出することとする。

Googleアカウントの準備

Colabを利用するためにはGoogleアカウントが必要である。基本的にECCSアカウントで問題ない。

Colabへのアクセス

以下のURLへアクセスする。https://colab.research.google.com/

ログイン後、次のような画面が表示される。

  • 最近使用したノートブック
  • Google Drive上のノートブック
  • サンプルノートブック

初めて利用する場合は「新しいノートブック」を選択する。

新しいノートブックの作成

画面左下の「新しいノートブック」をクリックする。 新しいノートブックが作成される。 作成直後のファイル名はUntitled0.ipynb となっている

左上のファイル名をクリックすると変更できる。

例えば本日の課題であれば、

day01.ipynb

のような名前に変更するとよい。


セルの実行

Colabではコードをセル単位で実行する。

例えば以下のコードを入力する。

print("Hello World")

セル左側の実行ボタンを押すとプログラムが実行される。ランタイム(=実行環境)に接続していない場合は、接続中...となり実行まで一定の時間がかかる。出力結果はセルの直下に表示される。

Hello World

Colabのインタフェースは直感的で親切であるが、いくつかショートカットを覚えておくと便利である。

ショートカット

セルを実行するショートカットキーは次の通りである。

  • Shift + Enter : 実行して次のセルへ移動
  • Ctrl + Enter : 実行のみ
  • Alt(Macだとoption) + Enter : 実行して新しいセルを作成

テキストセルを入れる

Colabではコードを実行するセルだけでなく、markdownによる記述を入力できる。上部にある「+テキスト」を選択すると、テキストブロックが挿入される。記述されている通りダブルクリックかEnterで編集できるので、「# 挨拶」と入力、改行して適当なコメントを記載してみよう

# 挨拶
まずはColabの使い方を確認する

その後、鉛筆マークで編集モードを閉じられるので、矢印でブロックを移動させ、さきほどHello Worldを書いたコードブロックの上部にこのテキストブロックを移動させる。このようにすると、テキストブロックが折りたたみ可能になり、テキストブロックとコードブロックが一つの塊になったことがわかる。markdownの内容は文章の階層に反映されるので、左側の「目次」をクリックすることで、ノートブックの文章階層を確認することができる。

Google Driveとの連携

Google Drive上にあるファイルをColabで扱う場合、GUIを用いる方法とコマンドラインでマウントする方法がある。

GUIを用いる場合、左側の下から2番目のフォルダ型のアイコンをクリックする。開いたサイドバーの上の方にサイクルマークのついた別のアイコンがある(ホバーすると「ドライブをマウント」と表示される)。こちらをクリックしアクセス許可をすることで自身のGoogle Driveをマウントすることができる。マウントするとコードセルからは以下のように参照することができる。

import os
os.listdir('/content/drive/')

CUIを用いる場合はコードセル上で以下のように入力する。一旦GUI上で「ドライブをマウント」のアイコンをクリックして接続を解除して、その後以下を実行する。

from google.colab import drive
drive.mount('/content/drive')

シェルの実行

コードセル内で!で始まるコードは接続している実行環境でコマンドを叩いた場合と同じ扱いになる。よってライブラリを追加インストールしたい場合はpipを使ったりできる。妙な話だがgccでC言語をコンパイルすることも実はできる。

! pip install パッケージ名
! printf "#include <stdio.h> \n int main(){ printf (\"Hello, World\"); }" > helloworld.c ; gcc helloworld.c ; ./a.out

ノートブックの保存

ノートブックは「ファイル > ダウンロード」で .ipynb を選ぶとダウンロードできる。本実験のレポートは最終的にこの形式で提出する。

Numpyを用いた数値計算事始め

まずはNumpyを用いた行列計算に触れながら、2日目以降の下準備を行っていく。

NumPyは、Pythonで数値計算を行うためのライブラリである。機械学習、データ分析、信号処理、シミュレーションなど、多くの分野で利用されている。

Python標準のリストでも数値データを扱えるが、Python自身の実行速度のため、大量のデータを処理する際には問題が多い。数々の線形代数演算も実装されているため、高速に処理する場合にはNumPyが非常に有効である。

NumPyの主な特徴は以下の通りである。

  • 多次元配列(ndarray)を扱える
  • 高速な数値演算が可能である
  • 行列演算を簡潔に記述できる
  • SciPy、Pandas、PyTorchなど多くのライブラリの基盤となっている

数値計算言語

Pythonを基準に考えると、数値計算 = Numpy となりがちだが、さまざまな言語を知っておくといざというとき便利である。数値計算に長けたプログラミング言語としては

  • MATLAB(およびそのクローン、例えばOctaveなど)
  • Julia

があげられる。C言語やRustなどのコンパイル言語の場合、何らかのライブラリやクレートのサポートが必要になるが、C++であればEigen、Rustであればndarrayが有名どころである。本資料も少し読みかえればこれらの環境でも実行できるため、興味がある人は試してみると良い。

NumPyの導入

Google Colaboratoryでは標準で利用可能である。

ローカル環境の場合はpipuvなどでnumpyを導入することになる。

pip install numpy

NumPyの読み込み

まずはNumPyを読み込む。

import numpy as np
慣例として np という別名を付けることが一般的である。


ndarrayの作成

NumPyではデータを ndarray と呼ばれる配列として扱う。

1次元配列

1
2
3
4
5
import numpy as np

a = np.array([1, 2, 3, 4, 5])

print(a)

出力例

[1 2 3 4 5]

演習1

次の配列をnumpyのndarrayとして作成して表示せよ。

[10, 20, 30, 40]

配列の基本情報

配列のサイズや形状を確認できる。

1
2
3
4
5
a = np.array([1, 2, 3, 4, 5])

print(a.shape)
print(a.size)
print(a.dtype)

出力例

(5,)
5
int64
属性 意味
shape 配列の形状
size 要素数
dtype データ型

演習2

以下の配列について、

  • shape
  • size
  • dtype

を表示せよ。

a = np.array([2.5, 3.1, 4.8])


2次元配列

行列形式のデータも同様に扱える。

1
2
3
4
5
6
A = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

print(A)

出力例

[[1 2 3]
 [4 5 6]]

行数・列数の確認

print(A.shape)

出力

(2, 3)

これは

  • 2行
  • 3列

を意味する。

reshape

さきほどのAについて3x2に変形する場合

np.reshape(A,[3,2])

演習3

次の行列を作成し、shapeを表示せよ。

[[10, 20],
[30, 40],
[50, 60]]

要素へのアクセス

Numpyについては、配列のインデックスは0から始まる。他の数値計算言語、特にFortran、MATLABやJulia(これらは1から始まる)に慣れ親しんでいる場合は注意する。

1次元配列

1
2
3
4
a = np.array([10, 20, 30, 40])

print(a[0])
print(a[2])

出力

10
30

2次元配列

1
2
3
4
5
6
7
A = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

print(A[0, 1])
print(A[1, 2])

出力

2
6

演習4

以下の配列から値 8 を取り出せ。

1
2
3
4
A = np.array([
    [5, 6],
    [7, 8]
])

スライシング

一部分だけ取り出すことをスライシングという。

1
2
3
a = np.array([10, 20, 30, 40, 50])

print(a[1:4])

出力

[20 30 40]

2次元配列のスライシング

1
2
3
4
5
6
7
A = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

print(A[:, 1])

出力

[2 5 8]

: は「すべて」を意味する。


演習5

次の行列から

[[4 5],
[7,8]]
を取り出せ。

1
2
3
4
5
A = np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])

配列同士の演算

NumPyでは要素ごとの演算が簡単に行える。

1
2
3
4
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a + b)

出力

[5 7 9]

四則演算

1
2
3
print(a - b)
print(a * b)
print(a / b)

演習6

以下を計算せよ。

a = np.array([10, 20, 30])
b = np.array([1, 2, 3])
  • a + b
  • a - b
  • a * b

ブロードキャスト

配列とスカラー値の演算ができる。また行列とベクトル値の間でも演算が可能。

1
2
3
a = np.array([1, 2, 3])

print(a + 10)

出力

[11 12 13]
1
2
3
4
5
6
a = np.array([
    [1, 2, 3],
    [4, 5, 6]
    ])
b = np.array([[1],[1]]) # 列ベクトル(2,1)
print(a-b)

出力

[[0 1 2]
 [3 4 5]]

こちらの演算は、例えばベクトルデータが入った行列から固定のベクトルを減算したりする際に有効である。


演習7

次の配列の全要素を2乗せよ。

a = np.array([4, 8, 12])

ブロードキャスト積

ブロードキャスト演算は、数学的に厳密に書き下すことは難しい。にも拘わらず、うっかりするとPython上で書いたブロードキャスト演算を論文の数式に普通の四則演算のように書いてしまうことがある。この問題を解決するために、松井は新しく「ブロードキャスト積」というものを提案しているので、論文中にブロードキャスト演算を表記する際は是非参照してみてほしい。


配列の生成関数

zeros

0で初期化する。

np.zeros(5)

出力

array([0., 0., 0., 0., 0.])

ones

1で初期化する。

np.ones(5)

arange

連番を生成する。

np.arange(0, 10)

出力

[0 1 2 3 4 5 6 7 8 9]

linspace

等間隔の値を生成する。

np.linspace(0, 1, 5)

出力

[0.   0.25 0.5  0.75 1.  ]

演習8

以下を作成せよ。

  1. 要素数10の0ベクトル
  2. 1〜20までの整数列
  3. 0〜100を11点で分割した配列

統計量の計算

統計量の計算は機械学習において特に重要となる。

a = np.array([10, 20, 30, 40, 50])

#平均値
np.mean(a)
#最大値
np.max(a)
#最小値
np.min(a)
#標準偏差
np.std(a)

演習9

以下について

  • 平均
  • 最大値
  • 最小値 を求めよ。
scores = np.array([72, 85, 91, 68, 77])

行列演算

行列にも通常の四則演算が適用できるが、行列積については注意を払う必要がある。

A = np.array([
    [1, 2],
    [3, 4]
])

B = np.array([
    [5, 6],
    [7, 8]
])

C = A @ B

print(C)

出力

[[19 22]
 [43 50]]

注意

* は要素ごとの積であり、行列積ではない。行列積には @ を使用する。


演習10

次の行列積を計算せよ。

1
2
3
4
5
6
7
8
9
A = np.array([
    [1, 0],
    [0, 1]
])

B = np.array([
    [3, 4],
    [5, 6]
])


乱数の生成

乱数はシミュレーションや機械学習で頻繁に利用する。

np.random.rand(5)

0以上1未満の一様乱数を生成する。


整数乱数

np.random.randint(0, 10, size=5)

特に機械学習では実データに対して、正規分布に基づくノイズが擾乱として加わっている仮定でモデルを考えることが多い。 そのため正規乱数の生成も非常に重要である。

正規乱数

# 平均0 標準偏差3 でノイズを10サンプル生成
np.random.normal(0,3,10)

演習11

1〜6の整数を10個ランダムに生成せよ。

ファイルの読み書き

Numpyで用いたデータをファイルやりとりする場合は以下のような関数を用いる。

書き込み

1
2
3
4
5
6
7
# 保存
a = np.array([[1,2],[3,4]])
np.save('my_array',a) # my_array.npy で保存

# 複数データ
b = np.array([10,20])
np.savez('my_arrays.npz',a=a,b=b) # .npzで保存

読み込み

1
2
3
4
loaded_arr = np.load('my_array.npy')

data = np.load('my_arrays.npz')
print(data['a'])

総合演習

次の処理を実装せよ。

  1. 0〜99までの整数を生成する
  2. 形状を (10, 10) に変更する
  3. 平均値を求める
  4. 2倍した行列を作成する
  5. 最大値と最小値を表示する

Python環境構築

本演習ではGoogle Colaboratoryを用いるが、余力のある人はローカル(手元のPC)でのPython環境構築に取り組んでみてもよい。Python環境構築は長年魔境であり、どうするのがベストプラクティスなのか毎年、議論が更新されていた。ここ数年はようやくこの争いが収まり、2026年現在は以下の2つが支配的となっている。

  • uv: Astral社による、Rustで書かれたPythonパッケージ・プロジェクトマネージャ。pipに相当するものを爆速で実行することが出来る。Pythonのみで完結するものに関しては、uvを使うとよいだろう。
  • pixi: anaconda(conda-forge)の流れをくむパッケージマネージャ。uvと同等の速度を持ち、またより広い能力がある。Python以外のことをする必要がある(c++プロジェクトもビルドする、など)場合はpixiを使うとよい。

機械学習とは

機械学習とは何かを規定する際、そもそも「学習とは何か」について述べる必要がある。少なくとも人間が何かを学習する過程を述べる場合、以下のようなことが考えられる。

  • 宣言的な知識(言葉でこれはこうだと規定できる知識)を新しく獲得する
  • 練習や教示を通じて、運動や認知の能力を発展させる
  • 構成された知識を一般的または効率的な表現へと持っていく(汎化と呼ばれる)
  • 観察や実験を通じて新しい事実や理論を発見する

一方機械学習の研究という文脈ではこれまで以下のようなことが考えられてきた。

  • タスクに基づいて、決められたタスクの性能を向上させるように学習システムを開発する
  • 人間の学習プロセスをコンピュータに基づいて模倣する
  • 数理的・理論的な解析により新しい学習アルゴリズム等をアプリケーションとは独立に探求する

特に昨今の「機械学習」のキーワードは、1つめの内容を指すことが多く、一方昨今の世間的な「AI」という言葉はまるで2つめの内容が実現されたかのように考えられている。人間の学習プロセスは最初にリストしたようにかなり抽象的なもので、一を聞いて十を知るといった風に、強い類推に基づいて汎化しているかのように語られている。では機械学習はどうか。

現状、機械学習は "Learning from examples" の実現という風に考えられている。すなわちあるコンセプトの事例となるデータが与えられた時に、それに基づいて何らかの規則・一般的なコンセプトを抽出するという流れである。この時人間の学習と同様、学習するもの(Learner)と教示するもの(Teacher)が存在し、データをどう捉えるかに基づいて、機械学習は大きく3つに大別される。

  • 教師あり学習: 与えられたデータが入力と出力の対になっており、最終的に入力に対応する適切な出力を求める問題設定
  • 教師なし学習: データのみが与えられ、データに存在する何らかの構造を抽出する問題設定
  • 強化学習: 状態と報酬、行動がデータとなるが、最終的に報酬が最大となるようにどのような行動を作るかの戦略を構成する問題設定

また近年では大規模なデータの増加に伴って、上記の分類の垣根を超えた方法論もいくつか登場している。例えばSemi-supervised learning(準教師あり学習)は、少量の高精度な教師ありデータと、多数のデータを組み合わせて実現される問題設定である。また自己教師あり学習は、データのみが与えられた条件において、自己データの部分復元や、データから抽出された構造の推定を教師あり学習の枠組みで行うことで最適な表現を学習する新しい枠組みである。

機械学習を学ぶ上で

昨今のディープラーニング、AIの発展から、実用上、機械学習を用いたアプリケーション等においては大規模なデータセットが必要不可欠となっている。しかしながら、モデルや理論を理解する上で、実データは必ずしも適切ではないこともある。その際、正解がある程度はっきりしている条件に対してノイズを付与し、データをシミュレーションする方法や、トイデータと呼ばれるある程度小規模なデータセットを用いたテストなどによって学習を進めていくことが多い。本日の最後に、以下に示す方法で、シミュレーションデータを生成し、ファイルに保存してみよう。

  1. 2次元データ(2次元ベクトル)の生成を考える
  2. x軸で 区間 -30 から 30(両端含む)で、200等分する
  3. \(y = 0.6 x + 0.2\) の直線関係が成立するが、観測時に正規分布ノイズが混入するものとする
  4. このような \(2 \times 201\) の2次元行列を生成し保存する

この手順は次回行う線形回帰の最も単純な場合を考えたものである。線形回帰は上記の条件でxからyを予測するタスクとなり、教師あり学習に分類される。

演習

上記の手順を実装せよ

おわりに

時間が余った場合は、Numpy公式の英語版チュートリアル にチャレンジしてみよう。日本語版の公式は1系で止まっているため、Numpy 2系でのチュートリアルを進める場合は、こちらを実行することになる。