質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

4554閲覧

パーセプトロンの重みの初期値を0でなく十分小さな乱数とする理由がわからない

luke04

総合スコア7

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2020/04/24 06:23

編集2020/04/24 06:30

#わからないこと
「達人データサイエンティストによる理論と実践 Python 機械学習プログラミング第二版」という本でパーセプトロンについて勉強しています。ここ(2.2.1 オブジェクト指向のパーセプトロンAPI、p24~)で、パーセプトロンを実装するときに重みの初期値を0とせず、正規分布に基づく小さな乱数で初期化しています。

Python

1import numpy as np 2class Perceptron(object): 3 """パーセプトロンの分類器 4 5 パラメータ 6 -------------------- 7 eta : float 8 学習率(0.0より大きく0.1以下の値) 9 n_iter : int 10 トレーニングデータのトレーニング回数 11 random_state : int 12 重みと初期化するための乱数シード 13 14 属性 15 -------------------- 16 w_ : 1次元配列 17 適合後の重み 18 errors_ : リスト 19 各エポックでの誤分類(更新)の数 20 21 """ 22 def __init__(self, eta=0.01, n_iter=50, random_state=1): 23 self.eta = eta 24 self.n_iter = n_iter 25 self.random_state = random_state 26 27 def fit(self, X, y): 28 """トレーニングデータに適合させる 29 30 パラメータ 31 -------------------- 32 X : {配列のようなデータ構造}, shape = [n_samples, n_features] 33 トレーニングデータ 34 n_samplesはサンプルの個数、n_featuresは特徴量の個数 35 y : 配列のようなデータ構造, shape = [n_samples] 36 目的変数 37 38 戻り値 39 -------------------- 40 self : object 41 42 """ 43 rgen = np.random.RandomState(self.random_state) 44 """ 45 重みを平均0、標準偏差0.1の正規分布に従う乱数で初期化 46 """ 47 self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) 48 self.errors_ = [] 49 50 for _ in range(self.n_iter): # トレーニング回数分トレーニングデータを反復 51 errors = 0 52 for xi, target in zip(X, y): # 各サンプルで重みを更新 53 update = self.eta * (target - self.predict(xi)) 54 self.w_[1:] += update * xi 55 self.w_[0] += update 56 errors += int(update != 0.0) 57 # 反復回数ごとの誤差を格納 58 self.errors_.append(errors) 59 return self 60 61 def net_input(self, X): 62 """総入力を計算""" 63 return np.dot(X, self.w_[1:]) + self.w_[0] 64 65 def predict(self, X): 66 """1ステップ後のクラスラベルを返す""" 67 return np.where(self.net_input(X) >= 0.0, 1, -1)

ここで、重みを0に初期化しない理由として、以下のように書いています。(p26, 27)

次に、重みを0に初期化していないのは、重みが0以外の値に初期化された場合にのみ、学習率η(eta)が分類の結果に影響を与えるからである。すべての重みが0に初期化された場合、学習率etaの影響を受けるのは、重みベクトルの(向きではなく)大きさだけとなる。三角法に詳しい場合はv1=[1 2 3]について考えてみよう。次のコードに示されているように、v1とベクトルv2=0.5 × v1の角度はちょうど0になる。

Python

1>>> v1 = np.array([1, 2, 3]) 2>>> v2 = 0.5 * v1 3>>> np.arccos(v1.dot(v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))) 40.0

(中略)乱数を(一様乱数などではなく)正規分布から抽出し、標準偏差0.01を使用した理由は、恣意的なものである。先に述べたように、すべての重みが0で初期化された場合のベクトルの特性を避けるために。小さな乱数値を使用したかっただけであることを覚えておこう。

この解説が理解できませんでした。もう少し詳しく教えていただけると助かります。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

同じアルゴリズムを、数式で書いている方を見つけました。
もしw(0)=0だったらどうなるか、も書かれています。
https://datascience.stackexchange.com/a/27305

読めば理解されるかもしれませんが、面白かったので、プログラムと対応させて説明してみます。
φ:predict()関数
w(0):初期の重み係数
y(1):forループ1回目(1つ目のサンプル)の目標(真値)
x(1):forループ1回目(1つ目のサンプル)の入力ベクトル
x(2):forループ2回目(2つ目のサンプル)の入力ベクトル
リンク先の一番最後の式がこちらです

φ((w(0)+η(y(1)−φ(w(0)・x(1)))x(1))・x(2))

この式は、質問者様のプログラムのforループの2回目(Xの2つ目のサンプルを学習する時)の、
update = self.eta * (target - self.predict(xi))
の中の
self.predict(xi)
が行っている計算を表しています。この時のpredict関数は、forループの1回目の重み係数の計算結果を含んでいるので、それらも展開して一つの式に示しているということです。
1回前の学習の計算の状態も同時に見ることで、質問者様の疑問が理解できるので、このように示されています。

まず、この式の注目する部分だけ抜き出しました。次の構造に注目してください。

φ((... + η(y(1) − φ(...)) ...) ...)

内側にあるφ(...)が、1回目のforループで計算されるpredictの結果です。
重み係数の初期値が0でなければ、このφ(...)は1か-1なので、
η(y(1) − φ(...))の部分は、η(y(1) − 1)またはη(y(1) − (-1))
y(1)の大小にも寄りますが、この項は正にも負にもなり得ます。
それをη倍した値が、外側のφの引数、つまり2回目のforループのpredictで使われています。
ここで言えるのは、w(0)とx(1)とx(2)の大小にも寄りますが、
ηの値が、外側のφの引数、つまり2回目のforループのφの引数の正負に影響を与えるということです。

φ((w(0)+η(正or負)x(1))・x(2)) = φ(ηによっては正or負)

ところが、重み係数の初期値が0であると、内側のφ(...)、つまりφ(w(0)・x(1))は、必ず1になります。
すると、ηの大小がどうであろうが、
ηの値が、外側のφの引数、つまり2回目のforループのφの引数の正負に影響を与えることは無いということが分かります。

φ((w(0)+η(y(1)のみに寄る値)x(1))・x(2)) = φ(ηが正負に寄与しない値)  (訂正しました)

これは、学習が3回目、4回目と進んでも同じことです。
これこそが、重み係数の初期値が0であると、ηが重み係数の向き(正負のこと)に影響しなくなるということの意味です。

ただ、w(0)が0でも、収束はしますよね。
それは、update=...の式で、重み係数の大きさ自体は変更出来ているからで、だから、ηは重みベクトルの大きさには影響を与えると。
具体的には、wが0の状態で1個目のサンプルによってw += η(y(1)-1)x(1)の大きさに変更されるからですが、これは要するに、重み係数の初期値が1個目のサンプルの入力ベクトルの定数倍から始まるような感じですし、学習率の調整も効かないので、局所解をすっ飛ばしたりはまり込んだりしても調整できないですし、学習効率が悪くなることも頷けます。

投稿2020/04/24 16:54

編集2020/04/26 12:03
akainem

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

luke04

2020/04/25 06:12

非常にわかりやすい説明ありがとうございます。ただ、重みを0で初期化した場合、内側のφは1になりますが、y(1)が-1or1なので、差は正でなく0またはー2ではないのですか?
akainem

2020/04/26 12:00 編集

あ、そうですね!失礼しました。ご指摘の通りです。 いつも正というわけではなく、x(1)に応じた値にならずy(1)のみに寄る値になってしまうということですね。 ηが正負に寄与しなくなるというロジックについては変わらずです。 回答の最後の式のところ、η(正) → η( y(1)のみに寄る値 ) に修正します。
luke04

2020/04/26 13:09

ですよね、返答ありがとうございます!
guest

0

あまり詳しくはないのですが、みんな同じ重みでスタートさせるよりも、少しずらしてスタートさせたほうが誤差が出やすく、学習率の向上につながるのではという考え方だと思います。

投稿2020/04/24 12:28

Luke02561

総合スコア404

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問