クーの自由研究

素朴な疑問を実験します。ボクと一緒に螺旋階段を頂上まで登ってみませんか?貴方の影についていきます。

計算機音楽の自由研究(準備:その5.3)~なんちゃって符号化器の設計と製造 ~

ちょっと気がかわってしまいました

こんにちは、こんばんわ。かえるのクーです。

f:id:np2LKoo:20170923212303p:plain

(c)池田真子様

そのまま、前作の位相方向を圧縮しても結果は予想できておもしろくないので、位相を圧縮するコンセプトはそのままに、(実験の結果をふまえ)「感性のみ」でつくったらどうなるかやってみます。
符号化器からかなり逸脱するかもしれないので、「なんちゃって」です。
完全に機械学習からそれた道草ですが、調子がよければ、音に関する標準的な検出器、生成器としてモジュール化しようと思います。

前作の反省から修正のポイント

 ・とにかく低音の切れがわるいです。窓枠をもっと小さくして検出します。
・情報が多すぎます。位相検出は16方位(22.5°)のままとしますが、位相を畳み込み、生成は0°のみにします。
・ウェーブレットのシフト幅は幅の1/4としていましたが、1/2とします。
・波形の再生はウェーブレットの合成を使わずに位相0°のsin波にウェーブレットで検出した値をエンベロープとして適用することにより合成します。
・波形エンベロープは検出2点間は基本的に線形補間します。立ち上がり閾値を設け、立下りしき値を設け、このしきい値をんまたぐエンベロープは形を補正します。(低音のキレを保つ工夫です)

プログラムデザイン

 理論にとらわれず自由な発想でやってみます。

お題 内容
プログラム名: なんちゃって符号化器
コンセプト1: 重み基底にウェーブレットを用いた局所的周波数検出を行う。
コンセプト2: ★符号化されたものは位相を畳み込んで圧縮する。★
コンセプト3: ★圧縮された符号から基底周波数ごとのエンベロープを生成する★
コンセプト4:

★曲起点の位相0°の基本周波数sin波とそのエンベロープを使い復号する。★

(これにより波形合成時のノイズをなくす)

コンセプト5: ★圧縮で犠牲にするのは「位相」情報と12KHz以上の音。★

 

重み基底に使うウェーブレットの設計

 前回とほぼ同じですが、一部違います。違うところを赤系にします。

周波数の範囲を下げて、ウェーブレットの幅を狭くしています。前回はA音を基準にしましたが、今回は他との関連ですこしかえます。

お題 内容
周波数バリエーション: ピアノの最低音Aからはじめ、★9オクターブ★の12半音(平均率)を準備する。
位相バリエーション: それぞれの周波数について、位相360°(2π)を16分割して準備する。
エンベロープ ウェーブレットのエンベロープはハン窓(ハニング窓)を使用する。
サンプリング: 48KHzを基準とする。
ウェーブレットの幅: 2のべき乗幅とし、オクターブ毎に幅をかえる。また、各窓は★8周期以上★のsin波が収まるようにする。(低周波ほど窓幅が広い)
重みの強度(振幅): 最終的な演算後の各周波数レンジのパワースペクトルが同じになるように振幅を調整する。2段の(リニア)乗算演算を行うため、ウェーブレット幅の平方根の逆数に比例するように設定する。
重みの数: 基底重みの数は12(クロマ)×★9(オクターブ)★×16(フェーズ)=1728(重み個数)となる。

 

入力前処理の設計

 同じです。

お題 内容
サンプリングレート変換: 内部で48KHzで演算するので、入力のサンプリングレートを48KHzに変換する。
チャンネル数: 内部でモノラルで演算するので、チャンネルを1にする(左右ミキシング)

 

エンコードの設計

 ほぼ同じですが、容量削減のための変更をしています。

お題 内容
重み枠シフト(スウィープ)幅: ウェーブレット重み幅の★1/2★とする。
使用する重み基底: 上記のウェーブレットのセットを用いる。
活性化関数: ランプ関数(ReLU)を用いる。
周波数上限 ★上限を12KHzとする。★
バイアス: 活性化関数にバイアスは用いない。
内部演算: 32Bitの浮動小数点とする。

 

畳み込みの設計

 今回新設。位相について畳み込む。

お題 内容
畳み込み計算: ★同一周波数について計算された活性化値を合計する。★

 

デコードの設計

 ウエーブレットのかさねあわせでなく、基本周波数のエンベロープ演算合成

お題 内容
重み枠シフト幅: エンコードと同じとする。
補完: ★シフト幅の1/4をリニアで補完する。★
使用する基本周波数: ★(窓ではなく)原音開始時点で位相0°のsin波を使用する。★
活性化関数: 恒等関数(Liner)を用いる。
バイアス: 活性化関数にバイアスは用いない
内部演算: 32Bitの浮動小数点とする。
周波数上限から24KHz: ★12KHzから24kHzは計算値を使用する。(3-6KHzと6-12KHzの情報から推定計算する)★

 

デコードのエンベロープ補完の補足

 今回新設

お題 内容
立ち上がり閾値 ★立ち上がり閾値をまたぐ値変化の場合は、リニアとせず、直前1/4幅ポイントのしきい値から立ち上がるものとする。★
立ち下がり閾値 ★立ち下がり閾値をまたぐ値変化の場合は、リニアとせず、直後1/4幅ポイントのしきい値へ立ち下がるものとする。★

 

波形の最終出力の設計

 前回と同じ

お題 内容
サンプリングレート: 一貫して内部48KHzで演算しているので、そのまま48KHzで出力する。
Bit幅: 16Bitに変換して出力する。

 

予想

 どこかに問題があれば、ひどい音になりますが、そこそいけるように思います。

そこそこが「いいかんじ」なのか「なしよりのあり」なのか、はたまた勘違いなのか、ボクとっても気になります。

プログラム

 いまからつくります。ウェーブレットや復号時ののエンベロープの計算が難しそうです。

計算機音楽の自由研究(実験:その4)~時には成功することだってある~

厳密ではないですが、原理的に離散ウェーブレット変換と等価です

こんにちは、こんばんわ。かえるのクーです。
(準備:5.2)で作成した『コレ(自己)ジャナイ符号』の性能をみてみます。

f:id:np2LKoo:20170922205913j:plain f:id:np2LKoo:20170922212222j:plain
f:id:np2LKoo:20170922214307j:plain f:id:np2LKoo:20170922214340p:plain
f:id:np2LKoo:20170922214324j:plain f:id:np2LKoo:20170922214401p:plain

 実験3で失敗の中に成功の片鱗がみえているので、あとはどれだけうまくいくのか興味です。

元にする曲:3分程度の曲を準備しました。

曲はYoutubeにアップしたけど、ブログ内キャンペーンもやったけど、公開してから半年以上たっているけど、今時点で総ビュー数「24回」を誇る「あの曲」です。

なお、このページでどれだけ再生してもYoutubeの再生回数はあがりませんので、あしからず。(このページに貼っているのはYoutubeでないので、あたりまえですが。)「Carpet of the sun」というプログレ好きの方ならたぶん聞いたことがある?Renaissanceの曲です。曲はプログレではなく、ブリティッシュなフォークトラッドです。歌っているのはミクです。

実験結果

SoundCloudの残り時間を温存したいので、復元した曲の最初の1分くらいのみをアップしました。(符号化器にかけるときにモノラル化していますので、復元もモノラルです。) 

復元音

 見事なまでに再現しています。もとの曲はずっとまえにSoundCloudにもアップしています。あたらめてはります。(こちらはステレオです。この曲を符号化器にかけて復元しています。)

元の音

よく聞かないと違いがわからないほどです。差があってもいわゆる「デジタル系」のブロック化ノイズではないので全然気になりません。(もとがステレオなので、比較は難しいですがほぼ再現できていると言っていいと思います。)f:id:np2LKoo:20170922202520p:plain上がエンコーダをとおして復元した波形。下2つが元の波形(左右チャンネル)です。モノラルになっているので、多少わかりにくいですが、ほぼ完ぺきといっていいくらいに波形がそろっています。

離散的な方法なので、必ず誤差が発生する方式での復元であるのに、この復元のレベルはすばらしいです。何度もきいていると、特に低域から中域にかけてのリズムやベースの「切れ」が、「かなり」そこなわれているのがわかります。まぁ、低域のウエーブレットのゆる~いエンベロープの重ねあわせで低域ができるので、無理はありません。が、そこも気をつけなければ、わからないくらいだと思います。

 

これは昨年の『「線形活性化関数」でも学習できる』件以来のクリーンヒットだと自画自賛状態です。もちろん、ボクが優れているのではなくて、離散ウェーブレット変換がすぐれていたのですが。最近の停滞ムードをかなり挽回できそうな感じがしてきました。 

符号化して圧縮したデータについて 

 

圧縮率を計算してみます。 

管理情報を除いた情報での計算です。 

元の音:(モノラル換算で計算)

44.1 KHz サンプリング 16it なので1秒あたり、882,000Bytes

符号化した情報:(こちらはモノラル、48Kにリサンプリングした情報で計算)

各周波数レンジについて12(半音)×16(位相)×1秒あたりの演算結果数( 48000(サンプリング周波数)/ ウェーブレットの幅(range0なら32,768)×4(幅あたりのシフト数))×4(32Bit浮動小数点の情報なので)

range = 0 (27.5 to 55 Hz):12 × 16 × 48,000 / 32,768 × 4 × 4 = 4,500 Bytes
range = 1 (55 to 110 Hz):12 × 16 × 48,000 / 16,384 × 4 × 4 = 9,000 Bytes
range = 2 (110 to 220 Hz):同様に 18,000 Bytes
range = 3 (220 to 440 Hz):同様に 36,000 Bytes
range = 4 (440 to 880 Hz):同様に 72,000 Bytes
range = 5 (880 to 1760 Hz):同様に 144,000 Bytes
range = 6 (1760 to 3520 Hz):同様に 288,000 Bytes
range = 7 (3520 to 7040 Hz):同様に 576,000 Bytes
range = 8 (7040 to 14080 Hz):同様に 1,152,000 Bytes
range = 9 (14080 to 28160 Hz):同様に 2,304,000 Bytes

合計 4,603,500 Bytes !!!

1秒あたり、4,603,500 Bytes です。

なんと約5.2倍になっています!圧縮どころではありません。演算のため16Bit整数ではなく32Bit浮動小数点にしているとはいえ、すこしデータが多すぎます。でも、これだけの情報をかけて符号化しているので、復元に優れているのは感覚的にしっくりきます。

圧縮符号化した情報はどんな感じ?

 エンコードした情報は、「12半音での周波数の解析結果が時系列にある」情報です。この情報があれば、

 ・もちろんそのままクロマグラム(12音解析)できる!
・そのまま縦横グラフにすれば、縦軸「周波数(対数)」横軸「時間」のスペクトログラムできる!
・局所的に周波数と位相に分離できているので、ちょっとした演算でイコライジングやエフェクトかけ放題
以下妄想
・基音が類推できれば、楽譜を生成できる!

・楽器の倍音構成が別に学習できていれば、さらに圧縮結果に自己符号化器をかけ、楽器音に分離して圧縮できる(かなりいい精度でトラッキング分離ができる「はず」)
・この情報さらにマルコフ解析やRNNとかパターン分析していけば、作曲パターンを分離抽出できる(ここは学習で!)

とりあえずやってみたいこと

 「圧縮」が目的ではないですが、2π1周を16分割しているので、この部分(位相情報部分)を畳み込めば、現状から単純に1/16に圧縮できるはずです。(今回は位相抽出用に各周波数16種のフェーズのウェーブレットを使用しています)

原音よりも容量が多くなっているのが意味もなく悔しいです。
興味本位で、圧縮率を高める方法をやってみます。

位相情報を畳み込むと、位相再生は完全に犠牲となり、原音復元からはずれますが。。。ボクとっても気になります。

 

たぶん次は「実験:4.1」くらいの表題にすると思います。 

計算機音楽の自由研究(準備:その5.2)~「自己」ジャナイ符号化器の設計と製造 ~

今度は成功の予感

こんにちは、こんばんわ。かえるのクーです。

f:id:np2LKoo:20170922091158j:plain

注)もちろんボク

ではありません。

モデルさんです。

ウェーブレットの重み基底の符号化器が「失敗」とおもいきや、なかなかの再現性能をみせたので、気を良くして長い音(曲)とかでも汎用的に実験できるよう、改めて設計してコーディングしてみます。(ウェーブレット専用)

「生きているから 明日があるから 地球がまわっているから」♪

プログラムデザイン

「重み」は学習しないのでこれはもう「自己符号化器」ではありません。ただの符号化器です。設計していきます。

お題 内容
プログラム名: コレ自己ジャナイ符号
コンセプト1: 重み基底に各種周波数、位相のウェーブレットを予め設定しておき、このウェーブレット重みに対して符号化を行う。
コンセプト2: 符号化されたものを、これも同様のウェーブレット重みで復号化する。
コンセプト3: 重みを学習しない以外は自己符号化器の構造をそのまま使用する
コンセプト4: 離散ウェーブレット変換と同等の符号化、復号化を行うため、ウェーブレット重みを所定の位置にシフトして演算できるようにする。

 

重み基底に使うウェブレットの設計

 W1、W2ともに準備する理想的なウェーブレットを使用します。演算幅が違えば「パワー強度」も違ってくるので、準備する重みのほうで予め補正調整しておきます。

f:id:np2LKoo:20170922173420p:plain

(ウェーブレットの幅が広ければ演算した結果が大きくなります。)

お題 内容
周波数バリエーション: ピアノの最低音Aからはじめ、10オクターブの12半音(平均率)を準備する。
位相バリエーション: それぞれの周波数について、位相360°(2π)を16分割して準備する。
エンベロープ ウェーブレットのエンベロープはハン窓(ハニング窓)を使用する。
サンプリング: 48KHzを基準とする。
ウェーブレットの幅: 2のべき乗幅とし、オクターブ毎に幅をかえる。また、各窓は16周期以上のsin波が収まるようにする。(低周波ほど窓幅が広い)
重みの強度(振幅): 最終的な演算後の各周波数レンジのパワースペクトルが同じになるように振幅を調整する。2段の(リニア)乗算演算を行うため、ウェーブレット幅の平方根の逆数に比例するように設定する。
重みの数: 基底重みの数は12(クロマ)×10(オクターブ)×16(フェーズ)=1920(重み個数)となる。

 

入力前処理の設計

 44.1KHz 2チャンネルの入力でもそれ以外でもすべて内部処理のレートに合わせます。

お題 内容
サンプリングレート変換: 内部で48KHzで演算するので、入力のサンプリングレートを48KHzに変換する。
チャンネル数: 内部でモノラルで演算するので、チャンネルを1にする(左右ミキシング)

 

エンコードの設計

 ランプ関数を使うところがミソでした。他のを使うと歪んだりノイズがでます。

f:id:np2LKoo:20170922165256p:plain

前回の実験で失敗していたのは、「ハイパータンジェント」を使っていたことも原因でした。(マイナス値となる演算結果がノイズとしてはいってくる)

お題 内容
重み枠シフト(スウィープ)幅: ウェーブレット重みの1/4幅とする。
使用する重み基底: 上記のウェーブレットのセットを用いる。
活性化関数: ランプ関数(ReLU)を用いる。
バイアス: 活性化関数にバイアスは用いない。
内部演算: 32Bitの浮動小数点とする。

 

デコードの設計

 最終段リニアにしたほうがいいのは、身をもって体験しました。

お題 内容
重み枠シフト(スウィープ)幅: エンコードと同じとする。
使用する重み基底: エンコードのウェーブレットのセットの転置を用いる(重み共有)。
活性化関数: 恒等関数(Liner)を用いる。
バイアス: 活性化関数にバイアスは用いない。
内部演算: 32Bitの浮動小数点とする。

 

波形の最終出力の設計

 内部演算した結果を、ほぼそのまま出します。

お題 内容
サンプリングレート: 一貫して内部48KHzで演算しているので、そのまま48KHzで出力する。
Bit幅: 16Bitに変換して出力する。

 

PG効率化の工夫

指定した音の前後にいちばん大きなウィンドウ幅の1/2サイズの無音をつけて処理し、復元したら、その無音部分を削除して出力する。そうすることにより、境界の考慮が不要になる部分があり、簡略化できる。(ただし、特に高音部分の配列で無音を担当する部分が増えるので、「若干」メモリ的に非効率になる)ループの外側であれば、多少速度や資源がエコでなくても、簡単にコーディングでき、シンプルなほうが修正もしやすくて幸せになれると思います。

予想

かなり視聴にたえるくらいのスペックにしたつもりなので、原音がかなりきれいに再現できると思います。 

プログラム

 実験用のコードをいれており、コアな部分以外でとても長くなっています。GitHubに置く予定はいまのところありません。


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
from scipy import signal
import audioop
import wave

"""
-------------------------------------------------
Korejanai Encoder V1.0 2017.09.22 Koo Wells
-------------------------------------------------
"""
IN_WAVE_FILE = ".\\sample20170920.wav"
DECODE_FILE = ".\\DecodeSound.wav"
BASE_SAMPLING = 48000 # 内部処理の基本サンプリングレート
BASE_WINDOW_SIZE = 2**15 # いちばん広いウェーブレット重みの幅
HALF_WINDOW_SIZE = int(BASE_WINDOW_SIZE / 2) # BASE_WINDOW_SIZE幅の半分
FASE_SPLIT = 16 # 位相は16分割(22.5°)
A0_NOTE_NO = 21 # A0(27.5Hz) Midi Note No. ピアノの一番低い音
A4_NOTE_NO = 69 # A4(440Hz) Midi Note No.
CROMATIC = 12 # 平均律12半音
WAVELET_SHIFT = 4 # 1/指定数 分づつシフト


# サウンドの読み込みと変換をします。
class soundtool():
def __init__(self):
pass

# 音のファイルを読み、基本的な情報を獲得します。
def fetch_soundData(self, filename):
wave_read = wave.open(filename, 'r')
w_channel = wave_read.getnchannels()
w_rate = wave_read.getframerate()
w_framenumber = wave_read.getnframes()
w_frame = wave_read.readframes(w_framenumber)
wave_read.close()
return w_channel, w_rate, w_framenumber, w_frame

# ストリームをサンプリングレート48KHz モノラルの音の配列(16bit)に変換します。
def conv48KMonoArray(self,aframe, achannel, arate):
if achannel == 2:
converted = audioop.tomono(aframe, 2, 0.5, 0.5)
else:
converted = aframe
new_frames = audioop.ratecv(converted, 2, 1, arate, BASE_SAMPLING, None)
array = np.array(np.frombuffer(new_frames[0], dtype="int16"))
return array

# ストリームをWaveファイルとして出力します。
def write_soundData(self, filename, stream):
s_write = wave.open(filename, 'w')
s_write.setparams((1, 2, BASE_SAMPLING, 0, 'NONE', 'Uncompressed'))
s_write.writeframes(stream)
s_write.close()

# ボクが欲しいのはコレジャナイ エンコーダ
class korejanai():
k_activation_Sigmoid = 'Sigmoid'
k_activation_ReLU = 'ReLU'
k_activation_Tanh = 'Tanh'
k_activation_Liner = 'Liner'

def __init__(self):
# 活性化関数とウェーブレットの設定
self.func = self.factory_activate_func(self.k_activation_ReLU)
self.func2 = self.factory_activate_func(self.k_activation_Liner)
# 21:MidiNote A0 27.5Hz
self.w1 = [
self.wavelet_init(2**(0/2), A0_NOTE_NO, A0_NOTE_NO + CROMATIC*1 - 1, FASE_SPLIT, 2**15),
self.wavelet_init(2**(1/2), A0_NOTE_NO + CROMATIC*1, A0_NOTE_NO + CROMATIC*2 - 1, FASE_SPLIT, 2**14),
self.wavelet_init(2**(2/2), A0_NOTE_NO + CROMATIC*2, A0_NOTE_NO + CROMATIC*3 - 1, FASE_SPLIT, 2**13),
self.wavelet_init(2**(3/2), A0_NOTE_NO + CROMATIC*3, A0_NOTE_NO + CROMATIC*4 - 1, FASE_SPLIT, 2**12),
self.wavelet_init(2**(4/2), A0_NOTE_NO + CROMATIC*4, A0_NOTE_NO + CROMATIC*5 - 1, FASE_SPLIT, 2**11),
self.wavelet_init(2**(5/2), A0_NOTE_NO + CROMATIC*5, A0_NOTE_NO + CROMATIC*6 - 1, FASE_SPLIT, 2**10),
self.wavelet_init(2**(6/2), A0_NOTE_NO + CROMATIC*6, A0_NOTE_NO + CROMATIC*7 - 1, FASE_SPLIT, 2**9),
self.wavelet_init(2**(7/2), A0_NOTE_NO + CROMATIC*7, A0_NOTE_NO + CROMATIC*8 - 1, FASE_SPLIT, 2**8),
self.wavelet_init(2**(8/2), A0_NOTE_NO + CROMATIC*8, A0_NOTE_NO + CROMATIC*9 - 1, FASE_SPLIT, 2**7),
self.wavelet_init(2**(9/2), A0_NOTE_NO + CROMATIC*9, A0_NOTE_NO + CROMATIC*10 - 1, FASE_SPLIT, 2**6),
]

self.w2 = [
self.w1[0].T,
self.w1[1].T,
self.w1[2].T,
self.w1[3].T,
self.w1[4].T,
self.w1[5].T,
self.w1[6].T,
self.w1[7].T,
self.w1[8].T,
self.w1[9].T,
]
self.z = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
]

# 活性化関数のファクトリ
def factory_activate_func(self, activate_func):
rtn_func = ActivateFunction()
if activate_func == self.k_activation_Sigmoid:
rtn_func = Sigmoid()
elif activate_func == self.k_activation_ReLU:
rtn_func = ReLU()
elif activate_func == self.k_activation_Tanh:
rtn_func = Tanh()
elif activate_func == self.k_activation_Liner:
rtn_func = Liner()
return rtn_func

# エンコード
def encode(self, x):
print('*** Encode now ***')
for i in range(len(self.z)):
print('range = %d (%d to %d Hz)' % (i, self.get_freq(i * CROMATIC + A0_NOTE_NO), self.get_freq((i + 1) * CROMATIC + A0_NOTE_NO)))
self.z[i] = []
w1 = self.w1[i]
ws1 = w1[0]
swin = x.size / ws1.size * WAVELET_SHIFT + 1
xwork = np.append(np.zeros(HALF_WINDOW_SIZE), x, axis=0)
xwork = np.append(xwork, np.zeros(HALF_WINDOW_SIZE), axis=0)
for s in range(int(swin)):
xwin = xwork[int(s * ws1.size / WAVELET_SHIFT):int(s * ws1.size / WAVELET_SHIFT) + ws1.size]
zwin = self.func.activate_func(np.dot(w1, xwin))
self.z[i].append(zwin)

return self.z

# デコード
def decode(self, z, acount):
print('*** Decode now ***')
x = [0] * (acount + BASE_WINDOW_SIZE + HALF_WINDOW_SIZE)
for i in range(len(self.z)):
print('range = %d (%d to %d Hz)' % (i, self.get_freq(i * CROMATIC + A0_NOTE_NO), self.get_freq((i + 1) * CROMATIC + A0_NOTE_NO)))
zwin = z[i]
w1 = self.w1[i]
ws1 = w1[0]
w2 = self.w2[i]
ws2 = w2[0]
swin = acount / ws1.size * WAVELET_SHIFT + 1
for s in range(int(swin)):
try:
xwk = self.func2.activate_func(np.dot(w2, zwin[s]))
except:
print('error 01')
print(s)
print(len(zwin))
try:
x[HALF_WINDOW_SIZE + int(s * ws1.size / WAVELET_SHIFT):HALF_WINDOW_SIZE + int(s * ws1.size / WAVELET_SHIFT) + ws1.size] += xwk
except:
print('error 02')
print(s)
print(ws1.size)
print(xwk.size)


return x[BASE_WINDOW_SIZE:acount + BASE_WINDOW_SIZE]

# ウェーブレットの初期設定
def wavelet_init(self, amp, astart_note, aend_note, afase, asample_size):
swav = np.empty(((aend_note - astart_note + 1) * afase, asample_size))
fs = BASE_SAMPLING
sec = asample_size / fs
freq = []
hann_window = signal.hann(asample_size)
for n in range(160)[astart_note:aend_note + 1]:
f = self.get_freq(n)
freq.append(f)
i = 0
for f in freq:
for fase in range(FASE_SPLIT):
s = amp * np.sin(2.0 * np.pi * (f * np.arange(fs * sec) / fs + (fase / FASE_SPLIT)))
swav[i] = s * hann_window
i += 1
return swav

# 指定周波数取得(平均律
def get_freq(self, n):
note = n - A4_NOTE_NO
freq = 440.0 * (2 ** (note / CROMATIC))
return freq


class ActivateFunction(object):
def __init__(self):
self.name = self.__class__.__name__

@classmethod
# @abstractmethod
def activate_func(cls, x):
raise NotImplementedError()

@classmethod
# @abstractmethod
# 微分関数は現在つかってませんが、そのうち実験で使うかもしれないので残しています。
def differential_func(cls, x):
raise NotImplementedError()


# ---------------------------------------------------------------------------------------
# Define the activate function
# ---------------------------------------------------------------------------------------
class Sigmoid(ActivateFunction):
@classmethod
def activate_func(cls, x):
return 1. / (1. + np.exp(-x))

@classmethod
def differential_func(cls, x):
return x * (1. - x)


# ---------------------------------------------------------------------------------------
class Tanh(ActivateFunction):
@classmethod
def activate_func(cls, x):
return np.tanh(x)

@classmethod
def differential_func(cls, x):
return (1. - (np.tanh(x) * np.tanh(x)))


# ----------------------------------------------------------------------------------------
class ReLU(ActivateFunction):
@classmethod
def activate_func(cls, x):
return x * (x > 0)

@classmethod
def differential_func(cls, x):
return 1. * (x > 0)


# ----------------------------------------------------------------------------------------
class Liner(ActivateFunction):
@classmethod
def activate_func(cls, x):
return x

@classmethod
def differential_func(cls, x):
return np.ones(x.shape)


if __name__ == '__main__':
print('--------------------------------------------')
print(' _( )_ ') print(' |□V□| コレジャナイ Encoder V1.0') print(' | - | 2017 Koo Wells') print('--------------------------------------------')
st = soundtool()
w_channel, w_rate, w_framenumber, w_frame = st.fetch_soundData(IN_WAVE_FILE)
print('*** Start ***')
print('SoundFile = ' + IN_WAVE_FILE)
data48KMono = st.conv48KMonoArray(w_frame, w_channel, w_rate) # 48KHz モノラルの配列にする
k = korejanai() # コレジャナイエンコーダオブジェクトの取得
z = k.encode(data48KMono) # エンコードして活性化値を求める
count = len(data48KMono) # データ長を求めて置く
x_hat = k.decode(z, count) # デコードする
amax = max(np.max(x_hat), -np.min(x_hat)) # 振幅の最大値を求める
decode = x_hat / amax * 32768 * 0.98 # 16bitでほどよく収まるように振幅を調整する
decode.astype('int16')
work = b''
out_stream = b''
amp = 0
print('*** convert Array to Stream Now ***')
# これが遅いのですが、いいコーディングを見つけれていません。
# 一発でInt配列からByteストリームに変換する方法が見つかりません。
for i in range(len(decode)):
amp = int(decode[i])
ampbyte = amp.to_bytes(2, byteorder='little', signed=True) # 地道に1サンプルづつ変換します。
work = b''.join([work, ampbyte])
if i % 10000 == 0: # 一旦ワークに入れるのはそのままjoinしていくと指数的に遅くなるからです。
if i % 100000 == 0:
print('stream:%d' % (i)) # 遅いので、経過をレポートします。
out_stream = b''.join([out_stream, work])
work = b''
out_stream = b''.join([out_stream, work]) # 最後の残りをフラッシュします。
print('stream:%d' % (i))
print('*** output Now ***')
st.write_soundData(DECODE_FILE, out_stream) #ファイルに書き出します
print('DecodeFile = ' + DECODE_FILE)
print('*** Complete ! ***')

300行程度まで整理しました。

(実験編へつづく)

計算機音楽の自由研究(実験:その3)~失敗はよくあることさ~

実験は失敗でした

 こんにちは、こんばんわ。かえるのクーです。
予告とおり、うまくいかなかった実験結果の報告をします。f:id:np2LKoo:20170919223929j:plain

初期重みにsin連続波形を使っても、最初はそこそこな再生音がどんどんホラーになっていく件

 音を自己符号化器にかけて復元する実験をしています。フーリエ変換などをかけずに直接自己符号化器にかけて復元できるかどうかを確認します。

自己符号化器でノイズはひどいものの音程がわかる程度に再現できたので、「正解」ともいえる「重み」を単一のsin波(のいろんな周波数や位相のセット)にしてそこを起点にして学習が良い方向に進むかどうかの実験です。

初期重み設定作戦

 

・初期重みWは(MNIST画像処理のときから)一様乱数を使用していました。
・画像処理の自己符号化器に強引に生の波形データを学習させたら、意外にも音程がわかるくらいに再現できるケースがありました。
・最初からsin連続波形を使ったときにそこにうまく倍音などが載っていき、きれいな再生音がでるかもしれないと思いました。
・W1(1層目の重み)はハイパータンジェント、W2(2層目の重み)は恒等関数(リニア)にして学習しました。

 

結果

原音、学習直後の復元音、1024(sub)epoch=約1epoch学習済の3パタン(0.8秒つづ)


学習をしない初期状態の自己符号化器が視聴上いちばんきれいな音を再生し、学習が進むにつれて不協和音やノイズの酷い音になっていきました。かなり急激に劣化したあとはそれほど音がかわらなくなるので、3パタンの音にとどめています。

 

考察

画像では「ガボールフィルタ」様の重みを自動的に学習していけましたが、音(の波形)ではまったく基本的なまとまった音(もしくは相殺してきれいな再生音になるような重み)は学習できませんでした。
文献にもそのような傾向(画像はうまく設定すれば、通常の自己符号化器でガボールフィルタが形成されるが、音の(通常の)学習でウェーブレットなどの基底が学習されるケースは知られていない)とありました。(文献のURLはどこかへ行ってすぐでてきません。あしからず)

現状のままでは「きれいな」特徴分離は無理があるとおもわれます。

ウェーブレットで重みを初期化する大作戦

ウェーブレットで波形をスウィープすれば、きれいな波形が自己符号化器でエンコード、デコードできる?かを確認しました。

ウェーブレットでの実験概要

ターゲットの波形:8bit 4000Hzサンプリング 3200sample(0.8秒)
調整したプログラム
以下のウェーブレット波形を重みとして作成(8bit 平均律半音)し、ターゲット波形の各ポイントに対してシフトしながら演算(行列直積計算)していく

・27.5Hz以上 1600sample(A音を境にしている)5点演算
・55Hz以上 800sample  9点演算
・110Hz以上 400 sample 17点演算
・220Hz以上 200 sample 33点演算
・440Hz以上 100 sample 65点演算
・880Hz以上 50 sample 129点演算
・1760Hz以上 25 sample 257点演算
それぞれの周波数に対して、45°刻みの位相、ウェーブレットのエンベロープとしてハン窓を使用

「27.5Hz以上 1600sample」のウェーブレットでターゲットの波形を800サンプル(ウェーブレット幅の半分)づつずらし、前後800サンプルはみでるのを含めて5回演算
同様に「1760Hz以上 25 sample」では12または13サンプルずつずらして、257回演算しています。

各ポイントでの演算は通常の自己符号化器の「エンコード」(W1層演算)、各ポイントで計算した値(行列)を、同様に自己符号化器の「デコード」(W2層演算)して復元していくものです。(デコード演算は恒等(リニア)関数の重ね合わせです)
できればバイアスだけでも学習させたかったのですが、「学習なし」でどうなるかを実験しました。

結果

原音-復元音の繰り返し×3音(各音0.8秒)

比較的似ている2番目の音について波形比較してみます。上がオリジナルで、下が自己符号化器での復元。

f:id:np2LKoo:20170920200308p:plain

エンベロープはすこし変動があるものの、まあいい感じです。

最初の部分を拡大します。同様に上がオリジナル、下が自己符号化器での復元です。

f:id:np2LKoo:20170920200353p:plain

位相はみごとにそろっていますが、振幅は微妙に差があります。

波形の図形にあわせて再現するのは位相の再現を最優先している感じです。(振幅が多少ずれるより、位相がずれたほうが差異(多次元ベクトルの直積差)が大きいため、位相をあわせることを優先してフィルタリング・合成している感じです:位相がすこしずれていただけでフィルタを通る値がとても減る)

中盤部分を拡大します。

f:id:np2LKoo:20170920200705p:plain

こちらも振幅は微妙に差がありますが、位相は上下でほぼそろっています。

ただし、波形でみるより実際の音の差異はとても大きく感じます。

違いが大きい3番目の波形を比較してみます。

f:id:np2LKoo:20170920201416p:plain

これも位相は合っていますが、振幅がかなり違います。視聴上の周波数特性もかなり違って聞こえます。

もっときれいな音になる予想をしていましたが、音程はほぼ再現できるものの、音程が揺れたり、かなり違った感じに聞こえました。
もっと位相やシフト幅をこまかくとれば、きれいな波形が再現できる「かも」しれません。

。。。

結局、追試することにしました。

スウィープシフト幅をウェーブレットの1/2から1/4へ、位相も45°(一周の1/8)刻みから22.5°(一周の1/16)刻みへ細かくして、もうすこしまともになるかやってみます。

 なんと、急によくなってしまいました。

原音-復元音の順に10音を抽出して再生しています。復元音は丸くなってはいますが、ほぼ原音の周波数特性や「雰囲気」を再現できています。ウェーブレットなかなかです。

こちらは、あながち失敗ともいえない感じがしてきました。

自己符号化器の「学習」でもこれくらいの再生ができるようにならないかな。。。

(誤解のないように改めて言いますと、この実験は「自己符号化器」のしくみは「ほぼ」そのまま使用しています。ただ使用している「重み」は「学習」したものではなく、「半音刻みの各周波数について、1/8分刻み(または1/16刻み)の位相シフトでのウエーブレット波形」を設定してのエンコード・デコード復元音です。「重み」が学習したものでないほかは、時間方向にシフト(スウィープ)しながら音をエンコード・デコードしている点がオリジナルの自己符号化器と異なります。符号化、復号化を自律的にやっていないので「自己」(Auto)がつかない単なる「符号化器」(Encoder/Decoder)です。)

ちょっと気力が回復したので、もう少し長い音をスウィープして再現してみます。

 (実験中:やたら長い音(曲の一部)でやったら配列(行列)の整合性が崩れて調整中:明日がんばる。長い音は一度に配列に入れずに部分的なスコープで見て、スコープをずらしていくことでどれだけでも長い音がエンコード・デコードできる気がしてきたので、宿題にします。:汎用的に今後もウェーブレット関連実験するならつくりなおしたほうがよいので、思案中(精度を極端に落としているので、実際の曲などでやってみるには精度がわるすぎる:内部は4Kzサンプリング 8bitまで落としているので。。。)

→すこし汎用的につくりたくなったので、「長い音」実験は保留してウェーブレット版符号化器を作成してみます。この実験編はこれにて一旦終了し、準備編にて、継続します。(このまま進めると、「まぁまぁいい感じだけど、曲のエンコードには解像度悪すぎる。」という結果になるはずなので、解像度(音だとなんていうのかな?)をあげて実験します)

PGができたら改めて実験編にて報告します。

感じたこと

・やっぱり、フーリエ変換やウェーブレット変換を真面目にやろうっと。

・ウェーブレット重みでの自己符号化器のエンコード、デコードについては、周波数、位相、窓枠が離散的です。離散ウェーブレット変換しているのとほぼ等価であると認識しています。

・学習「初段」は、逆伝搬でなく、SOM(自己組織化マップ)させたい。似た情報は自律的に(逆伝搬の方法でなく)順伝搬の過程で統合して処理学習していくのがよいと強く感じました。

 

最近気になること

コネクトーム」がとっても気になります。

あ、ちなみにボクが深層学習とかRNNやるのはもっと、たぶんずっと先です。

自己符号化器で精一杯です。

計算機音楽の自由研究(準備:その5.1)改めて自己符号化器をサウンド用に最適化する

停滞すること幾年月

こんにちは。かえるのクーです。

手痛いです。今年はさっぱりです。参考にできる「ちょうどいい」情報が少ないです。サウンドに関する情報は超アカデミックか、(あたりまえですが)フーリエ変換やウェーブレット変換を前提にしたものばかりです。

f:id:np2LKoo:20170916102413j:plain

フーリエ変換やウェーブレット変換自体の機能(周波数解析)を自己符号化器に「実質的」にやらせよう発想がそもそもおかしいのは自覚しています。でも、やれる?という「素朴な疑問」を持ってしまったので仕方ありません。「素朴な疑問をやってみる」のがこのブログのコンセプトでもあります。

うまくいっていない点

波形がプラスとマイナスで形がかなり違い、歪んでいる。

ノイズ成分が収束していかない。

どうやっても(うまくいく例で)原音の音程がわかる程度。(なので最初「そこそこ」うまくいったのは「たまたま」だったようです)

さて何が悪かったのでしょう

波形の歪については、分かってみればあたりまえですが、一番の原因は「活性化関数」でした。どの参考書をみても「最終段は恒等関数(リニア)がよい」を書いてあるにもかかわらず、1段目と2段目の関数は同じ関数しか使えないようなプログラムでした。

そのため、最終波形は、「活性化関数のリミッター」がかかったような歪んだ波形になっていました。(ノイズ成分も一律歪む:マイナスが抑制される活性化関数ではマイナス側が極端に歪む)

雑音については、最初の乱数で発生している「高い周波数」の雑音相殺や丸め込みがどうしてもできないので、(雑音が)収束していきません。

いろいろ、うまくいかないので、どうすればいいかぼんやりとずーっと考えていました(ボクはこれを思考実験と呼んでいます;p)。そろそろまとめにはいって進めないと、冬が来てしまいます!(熊さんはそろそろ冬支度の活動をはじめているようです。)

改善のポイント(として考えたこと)

・各段での活性化関数を自由に設定できるようにする。

・最終段の活性化関数はリニアもしくは原点対称のゆるやかな(リニアに近い)非線形関するとする。

・W初期化は一様分布の乱数を指定しているが、特定基底周波数のsin波形、もしくはウエーブレット波形を初期値とする。

・W初期化としてsin波形を使う場合、振幅は(いんちきですが)元波形に近い減衰エンベロープのものを準備して改善されるかやってみる。

・W初期化としてウエーブレット波形を使う場合は、周波数のほかに「位置」のバリエーションを考慮する。このため、W配列の個数を増やすか、W配列をコンパクトにして任意の位置で計算できるようにする。

f:id:np2LKoo:20170916101931p:plain

 ・コーディング力があれば、Wをすべて短いウエーブレットとし、波形全体をスウィープさせて局所的な活性化値を求め、復号は逆に局所的な活性化値からウエーブレット合成で行うことでできるようにすれば、かなりの復元能力が見込める(はず)。(これはもう自己符号化器といえるか疑問ですが。。。)

この3連休がんばってみます。

(あとでできぐあいをここに書きます。実験報告できるようなら実験編をかきます。)

経過報告

 まずは、重みwの初期値をsin波にしてやってみました。

平均律の周波数のsin波88個(ピアノの鍵盤の個数)準備して、1音程につき、位相を45度づつずらしたものを8個準備してwにセットして学習してみました。

期待としては、各sin波に学習対象の音(ピアノ、ストリングス等)の楽器的な倍音がのっていき、その組み合わせでいろいろな楽器の音を「そこそこきれいに」形成できることを理想としていました。

結果は、「どうがんばっても、sin波に不協和音的な周波数やノイズ的音が載っていき」、出力するときにその不協和音的周波数を相殺しきれずに、学習すればするほど元の音から離れていきます。(涙)

自己符号化器はsin波の重ね合わせで音を表現できることを知らないので、無理もないのですが、wが初期値sin波のままの、まだ「学習していない」状態がいちばんきれいに聞こえました。

音によっては途中までかなり波形的に近くなるケースもあるのですが、学習をつづけるとどの音も「ホラー映画の不協和音なアンビエント音」になってしまいます。

たぶんですが、

・ある程度の長さの持続的音程波形であれば、自己符号化器はフィルタにsin波を設定すれば、かなり精度のよい検出器として機能できる。

・自己符号化器は「そのままのしくみでは」重みWについて、基底として純粋なsin波や倍音を学習していくことは困難である。

・耳は伊達に物理的フーリエ解析機能を持っているわけではない。必要だったからそう作っていったのでしょう。(飛躍ですが、そう感じたので書きました)

(連続)sin波重み初期化作戦は初期値以上によくなる学習ができそうにないので、ウェーブレット作戦に移行します。

 

次にウェーブレット重み抽出解析(変換ではない)をやります。

これはプログラムをかなり書き換えないといけないので、がんばります。こちらは学習機能を確認するのではなく、抽出機能としてどれくらいできるのかを確認するものです。(音も画像の学習のように「ウェーブレット(ガボール)フィルタ」を学習して取得できるようになればおもしろいのですが、たぶん今の流れでは難しそうですので、抽出機能の確認に留めます。)

さて、ウェーブレットのページをいろいろ見ましたが基底の波形を作る計算式が厳密にやると少し(ぼくにとっては)難しいので、近似します。

・波形のエンベロープは「ウェーブレット」の計算式ではなく、ハニング窓の式で代用(近似)する。「マザーウェーブレットは自分で定義してもいい」と書いてありました。

 ・スケール(伸縮)については自己符号化器のWのサイズをある程度固定にしたいので、段階的にする。(真面目にやるとWのサイズが全部違ってくるので、行列計算にならないため)

例:100Hzまで800サンプリング、200Hzまで400サンプリング、、、800Hzより上は50サンプリングなど、基底周波数帯域によりWのサイズをかえる。

 ・トランスレート(平行移動)については、Wのサイズに従った基本的移動と組み合わせる。(Wサイズの1/2または1/4)

 ・基底周波数の位相にいては45°単位で基底周波数ごとに8パタン持つことにする。

これぐらい簡略化すれば、(数学的飛躍はともかく)方式の意味合いを崩さずにボクでもコーディングできるような気がします。

なお、今回お勉強したのは以下のサイトです。理解するまではいきませんが、今回は雰囲気がわかる程度でいけると思います。

とある出版のサンプル

とある大学の講義補助資料

とあるメーカの技術解説資料

 わ~、できそうと思ったのが気のせいでした。汎用性をもってコーディングしようとするとめちゃ難い。汎用性を無視して周波数帯ごとに区切ってにまわせるか試行中ですが、むりぽです。

難しいので、学習機能を完全に削除して純粋にウエーブレットでエンコード、デコードのみをするプログラムにしました。位相45°単位では波形を見る限りはそこそこいけているのですが、音を聞くと全然でした。

思ったようにはどちらもいかなかったのですが、実験結果として改めてレポートします。(失敗系のレポートです。記録として書きますが、きっと、読む価値がありません。「めげた」ので明日書きます。)

思うようにいかなかった点

サイン波の重み初期化実装:それぞれのサイン波に楽器とにたような倍音がのっていくことを期待→倍音がのっていくことはまったくなく、ノイズや不況和音がのっていくのみ。最初がいちばん原音に近く、学習すると(学習といえないですが)再生音が崩れていく感じです。

ウェーブレットでの再現:もうすこし原音に近い音がすると思っていましたが、波形はかなり似るものの、音を聞くとまったく別物でした(基底周波数はあっているけど、音色がまったく違う感じ)音は波形が「似ている」くらいでは全く同じように聞こえないことを改めて認識しました。

来月は1ヶ月不在になっちゃいました

スマホは持っているのですが、おでかけ用パソコンを買いました。どうしてもスマホの入力が苦手なのです。音声入力や、小型Bluetoohキーボードも使ってみましたが、どうしても入力や編集がしっくりきません。操作を考えているうちに、考えがどこかへ飛んでいってしまいます。何も考えなくてもそのまま入力できる(くらいに慣れている)パソコンがやっぱりいいです。

ノートパソコンは高くて買えないので、スティックタイプのパソコンを探しました。

最低でもメモリ4GB、Windows64Bitのものをさがしましたが、そのスペックでスティックタイプのは高かったので、「ボトルタイプ」の持ち運べるものにしました。

これです。

f:id:np2LKoo:20170916095105p:plain

ドスパラという会社で扱っている

デジノスというシリーズです。

税別、送料別で14,800円でした。

 おこずかい的にはちょっと厳しかったですが、今月は「本」の購入を我慢します。

ワイヤレスのキーボードとマウスがついて、OSもプリインストールされていて音も出て簡易カメラもついて、バッテリーもついてこのお値段ならまぁ、いいかな?という感じです。

現メイン機(Core i7-2700K)の1/3くらいのスピードでメモリも4GBしかないですが、お出かけ用としてはOKかなとおもいました。今はこれにPycharmとかanacondaを入れて軽い実験はできるようにしています。

お出かけ先には「テレビ」はあるので、HDMIでにつないでディスプレイにします。

 

 

 

 

 

おたまじゃくしさんの自己符号化器~ノードは気長に学習していく編~

今回はイメージでみじかく説明します

全国の「おたまじゃくし」のみなさん。そして、全国の小中学生のみなさん。

こんにちは。こんばんわ。

引き続きボク「ノード」ガ説明します。今回は「学習」についてです。
前回はむだにがんばりすぎて長くなり、読むのがたいへんでした。ごめんね。途中(とちゅう)で読むのをやめちゃったお友達もいるかもしれません。

f:id:np2LKoo:20170909110300j:plain


今度は(前よりは)短くいきますね。すこし興味(きょうみ)があったら、前回の説明も改(あらた)めて、読んでね。(わかりにくくてごめんなさい。)

結局、神経接続(しんけいせつぞく)とか、自己符号化器(じこふごうかき)とかは何をしているの?

神経接続や自己符号化器の、「なんとなく」のイメージを書きます。キーワードは

エコ

無駄をとことん省く。やらなくても「なんとかなる」ならやらない。

少しで、たくさんのことをあらわす。

全部「覚える」のはたいへんなので、「似(に)たもの」は共通しておぼえる。(まとまろうとする)

最適化(さいてきか)でちょうどいいかんじ

それぞれを見ると「いまひとつ」でも、全体で「ちょうどいい」感じにしていく。

逆に、部分だけに注目することもある。(注目といっても自動的にそうなります。「考えて」そうするわけではありません)

ぎゅーっと圧縮(あっしゅく)して「最適化しているときに夢をみる」という説もあるよ。

ちょっとづつゆっくり学習する

たくさんの入力データから、すこしづつ「学習」する。時間がかかる。

幅広く対応(汎化:はんか:(おうようりょくがあること、ものの一般的な性質をとらえること)+スケーラブル(ちっちゃいのをめっちゃ大きくまでできる))

みたことがなくても、似たデータであれば、対応できるようになる。

必要に応じて、つながる細胞や〇ノードをどんどん増やせる。

混沌(こんとん:カオス)としていて柔(やわ)らかい

結果はそれほどかわらなくても、中身はたえずよくなるようにチャレンジしているよ。
つないだり離れたり。わるく言えばかなり「ごちゃごちゃ」している。

よく言えば、どんなものにも対応できるようにやわらかく形をかえていける。長く覚えたいものや学習してしまったものは「固(かた)め」だよ。

より精度(せいど:正確さ)や、より「広い範囲の対応」が必要なときは、神経細胞や〇ノードをどんどん追加することにより、どこまででも対応できます。(でも実際は細胞やパソコンの容量(ようりょう)に限りがあるから、無限というわけではないよ)

学習したらどうなる?

学習したあとの「答え」を先にみて、逆から考えてみましょう。

f:id:np2LKoo:20170903210218p:plain

学習した自己符号化器は以下のような形になります。

Xの部分

情報がはいってくる部分です。毎回データがはいってくるところで、とくに学習前後でかわりません。

W1の部分

「抽出器」「フィルタ」「分析器」「情報をとりだす」「その形の情報がどれだけ含まれるか調べる」部分として情報がまとまります。

どんな形の情報をとりだしたらいいのかが学習されています

1つだけではよくはたらけませんが、たくさんの部分が協力して情報を取り出します。

Zの部分

ZはW1であらわされる「形」「くみあわせパターン」がどれだけ含まれるか、の値になります。
もちろん「形が」近いと値が高くなり、違いが大きいとゼロとか、とても小さくなります。

W2の部分

「合成器」「生成器」「情報を復元(ふくげん)する」「情報をつくりだす」部分です。

組み合わせる「もとになる形」が学習されています。「それぞれの形」はいろいろな形を再現する「素材(そざい)」「もとネタ」として使われます。

Yの部分

YはW2であらわされる「形」や「パターン」を合成して元に近い状態(じょうたい)にしたものです。

神経細胞でいえば、「XとW1」がセットで視神経(ししんけい)、「ZとW2」がセットで視覚野(しかくや:後頭部(あたまのうしろのほう)にある)の脳神経(のうしんけい)、「Y」が2段目の脳神経で、たぶん3段目の脳神経にたくさんつながります。(今回のモデルでは3段目までは考えません)自己符号化器は元にもどすしくみですが、脳細胞はさらにいろいろ繋(つな)がっているようです。(ずっと前に見たものを思い出せるので、復元(ふくげん)するしくみもあるはずです。)

自己符号化器は、入力データからこのW1の「フィルタ」のパターンと、W2の「合成元」パターンを自動的に調整し、学習していくものです。フィルタのかかり具合や、合成器の敏感(びんかん)さも、B(バイアス)として調整します。
実はフィルタと合成元は同じでいいんじゃない?という考えもできます。これが前回もでてきた「重み共有」というやり方です。

ただし、重み共有は、入力と同じものを出力したいときは使えますが、入力に対して別のものを学習して出力したい場合には使えません。

フィルタって何?

必要なものだけを取り出すしくみの「もの」です。たとえば次のようなものがあります。

コーヒーフィルタ コーヒーだけを通します。
カメラのフィルタレンズ

紫外線(しがいせん)や水面の反射光(はんしゃ)など余分(よぶん)な光をさえぎります。(きれいな写真がとれます)

マスク

風邪(かぜ)をひいたとき:咳(せき)やくしゃみで、飛沫(ひまつ)がとびちらないようにします。空気は通します。

花粉の時期:花粉をすいこまないようにします。空気を通します。

 数字のフィルタってどんなの?

0から9までの10個の数字を、いちばんよく通す「フィルタ」を考えてみます。
光を「とおす」フィルタで考えてみます。

いちばん光を通すフィルタは透明なフィルタです。
逆に光を通さないフィルタは、何を入力しても真っ暗です。
1の形をよく通して、他の方をとおさない図形を考えてみましょう。
1の形そのもの図形ではどうでしょうか。f:id:np2LKoo:20170909164222p:plain

1の形は全部通ります。通る光の量はその数字「そのもの」が一番多くて明るくなります。
他の数字は重なっていないところは光がとおらないので、似ていない数字は明るくありません。なんとなく形がつながっている、4とか7とかが1に近いかもしれません。興味深(きょうみぶか)いのは3のフィルタを通した図形と8のフィルタを通した図形が似ていることです。逆に考えれば3と8は(たまたま選んだ)1からみれば(どれだけ自分ににているかという形として)そっくりなのかもしれません。そんな見方をすと、5と6と9も似ている気がします。(手書きの文字だと、もっともっといろんなパターンがありそうです)

フィルタは分析器(ぶんせきき)といっていいかもしれません。

合成器(ごうせいき)ってなに?

複数の素材(そざい)から別のものを合成して作るどうぐです。ある意味、魔法(まほう)です。

コップ レモンをいれて、はちみつ入れて、氷をいれて、炭酸水(たんさんすい)をいれて、ストローさして、レモネードのできあがり!
おなべ いろんなものいれてぐつぐつにて、カレーのできあがり!
ジグソーパズル(の枠) ばらばらのピースをくみたてて、やった!完成(かんせい)だ!

合成するといっても、

まんべんなく混(ま)ぜてできあがるもの(いれた素材が全体にひろがる)

元は残るが、一部は全体に広がるもの(じゃがいも溶けてちいさくなった~)

そのままある部分だけをくみあわせるもの(機械(きかい)の部品(ぶひん)とか)

今回の数字はフィルタをそのまま合成器にしようと思いますので、重ね合わせてみます。元の数字になるかな・・・?う~ん微妙。たしかに1がいちばん目立っていますが。。。

f:id:np2LKoo:20170909165535p:plain

もとの数字をかさねあわせただけだとどうしてもうまくいきません。

「どうすればいいと思うか」は宿題にしちゃいます。(クーのブログを読んでくれれば答えがわかるかも)

ヒント:バイアスってどんなはたらきがあるんだっけ?

学習ってけっきょく何なの?

「お料理をまったく知らない生徒が先生に教えてもらう」ことで考えてみましょう。

「フィルタと合成器」をあわせて「生徒」とします。「舌」がフィルタで、「おなべ(使い方もふくめて)」が合成器のイメージです。お料理なので、入れたものとでてくるものは違いますが、説明のためのイメージとして考えてください。入力と出力が違うので「重み共有」はつかえませんよ。

「とあるお料理教室の日常」


先生:これとまったく同じお料理をつくってください。

生徒:よくわかんけど、そのまま全部「なべ」にいれてみよう。先生できました。

先生:素材が大きい。味付けが薄(うす)い。煮込(にこ)みがたりない。-1090点

生徒:マイナスの点数ってきいたことないです!何点満点(まんてん)なのですか?

先生:0点満点です。マイナスは限度(げんど)がありません。

生徒:具体的(ぐたいてき)に何をどうすればいいか教えてください。そうしないとできません。

先生:私が教えることができるのは、このお料理(正解)との差と点数だけです。

先生は具体的にどのようにすればいいかまったく教えてくれません。先生は「正解との差」と「評価(ひょうか)」のマイナスの点数(減点)だけを教えてくれます。次にやるときには、先生からの情報(ヒント)をもとに、もう少し調整してみることにします。調整(ちょうせい)する「量」は自分できめなくてはいけません

先生:では次のお題です。

生徒:え??今のをとことんやるんじゃないんですか?

先生:ひとつのことだけやっていたのでは、そのお料理しかできません。このお料理教室は、「ありとあらゆるお料理をできるようになる」ための教室なのです。

生徒:あ、生徒募集(せいとぼしゅう)のちらしにはたしかに「これで、なんでも作れちゃう!彼氏のハートをゲットだぜ!」って書いてありましたが。。。

f:id:np2LKoo:20170909150722p:plain

先生:「ゲットではなく、☞ゲッツとかいてあったはずです」生徒の皆さんには「料理のなんたるか」を学習していただき、なんでもつくれるようになってもらいます。

f:id:np2LKoo:20170909150745p:plain

生徒:(皆さんって、生徒私ひとりなんだけど)本気卍なのかぁ。。。

先生:「まじ」なんて言葉使わずに。「ほんき」といってください。「根性(こんじょう)」があればどんなことでもなしとげられます。

生徒:(うわぁ~。昭和だぁ。)

。。。

。。。

生徒:できました。

先生:お肉はややちいさいです。玉ねぎだけ煮込(にこ)みが足りませんが、他はやや煮込みすぎです。すこし塩分(えんぶん)が多くて、砂糖(さとう)はわずかに少ないです。-50点

生徒:おお、また-100点を切れました。
先生:よくここまでがんばりました。もうお料理の本質(ほんしつ)は会得(えとく)したといってよいでしょう。30年間よく諦(あきら)めずにがんばりました。

生徒:先生、でもまだ彼氏できないんですが~

先生:それは「お料理」の課題(かだい)ではありませんので、私にはわかりません。でも、免許皆伝(めんきょかいでん)なので、あなたもお料理教室はじめてはどう?

生徒:でも彼氏がまだ、、、

先生:学習とは誰かに「やり方」を教わるものではないの。自ら、ありのままの入力と出力と目標(正解)から会得(えとく)していくものなのよ。先生はヒントをくれるにすぎないわ。コーチングの神髄(しんずい:もっともかんじんなところ)でもあるわね。

。。。

新米先生:「なんでも作れるようになるお料理教室。本日開講(かいこう)!これで彼氏のハートをロックオン!」

f:id:np2LKoo:20170909151114p:plain


お料理の「達人」は、ある「お料理」を口にしただけで、素材(そざい)や調理法(ちょうりほう)がわかるといわれています。
音楽の「達人」は、ある「音楽」を聞いただけで、そのまま弾いたりアレンジしたりできます。

練習、下積(したづ)みでいろいろなことを学習し、どんなときに何をするか、どう組み合わせるかの「パターン」を経験(けいけん)で知っているのです。

今回は「なんとなく」の説明になりました。「学習」はどうして(いろいろな音や映像(えいぞう)や行動(こうどう)について)そのように(未知(みち)の情報に対してもある程度対応したり)できるのかについては、まだよくわかっていない(数学的に説明しきれていない)部分もあります。でも論証(ろんしょう)されていなかったり、実証(じっしょう)されていないだけで、かなりの部分がどんどんわかってきているんです。(論証や実証して支持(しじ:サポート)がもらえ、反論(はんろん)がなくなって、誰もが認(みと)めるようになっていけば本物だよ)

機械学習めっちゃ楽しいよ

興味(きょうみ)があったら「かえるのクー」といっしょに勉強していこうね。

 

 おたまじゃくしさんシリーズはひとまず完了です

「〇ノード」くんどうも有難う。教えるのって難しいですよね。

さて、「おたまじゃくし」シリーズをはじめたころは生まれたてだった「おたまじゃくし」さんも、そろそろ後ろ足がでてきて「肺」で呼吸ができるようになってきました。

みなさん。たくさん食べて、大きくなって、冬に備えてください。

「おたまじゃくし~かえる」の青春期(せいしゅんき)はほんとうに短いです。かえるになってからでは覚(おぼ)える能力(のうりょく)や理解力(りかいりょく)は頭打(あたまう)ちになってしまいます。『「おたまじゃくし」のときにどれだけ頑張(がんば)ったか』、でそのあとの能力がきまってくるところがあります! まだかえるになりきっていない「おたまじゃくし」さんは騙(だま)されたとおもって、「いまやっていること」に「ぜんりょく」で(そして自分で納得(なっとく)できるまで)とりくんでみませんか。きっといいことがあります。では、今年のおたまじゃくしさんシリーズは、これにて閉校(へいこう)です。来年も、もしかしたら(実験にいきずまったら)「開校(かいこう)する」かもしれません。今年のおたまじゃくしさんは、来年はおたまじゃくしではなく、「かえる」の立場でこのブログを見てもらえると嬉(うれ)しいです。

どうも有難(ありがと)うございました。

 

おたまじゃくしさんの自己符号化器~クールに説明するノード編~

新学期たのしいね

全国の「おたまじゃくし」のみなさん。そして、全国の小中学生のみなさん。
いよいよ新学期ですね。
日に日に夕暮(ゆうぐ)れがはやくなってきていますね。

こんにちは。こんばんわ。ボク「かえるのクー」といいます。

さて、今回は、神経(しんけい)の接続(せつぞく)を「モデル」で表してみます。モデルとは「模型」(もけい)みたいなものだよ。いろんな洋服の似合う、かっこいいおねーさんも「モデル」ということがあるけど、今回はそちらではないよ。
神経接続」をモデルであらわしたものをニューラルネットワークといいます。NNえぬえぬ)と略(りゃく)すこともあるよ。
ニューラルネットワーク(NN)を説明するとき、すこしだけ専門用語(せんもんようご)がでてくるけど、一度イメージを覚(おぼ)えてしまえばそんなに難(むずか)しくないと思います。

ニューラルネットワーク(NN)は神経接続をまねて、これ以上できないくらい単純(たんじゅん)にしたものです。

接続の形は似ているけど、実物(じつぶつ)のニューロンとはそんなにも似(に)ていないかもしれません。
どんなところが似ていて、どんなところが違うのか興味(きょうみ)があったらこの記事(きじ)を読んでみてください。(すこし長いけど最後まで読んでくれたらうれしいな)

「ノード」と「エッジ」。聞きなれないけどボクタチノコト覚えてね。

では今日は自己符号化器(じごふごうかき)の「部品」(ぶひん)でもある「ノード」くんに説明してもらいましょう。「ノード」くんどうぞ

f:id:np2LKoo:20170903135505p:plain

「ノード」くん自画像

こんにちは。ボクハ「ノード」といいます。ノードの説明をするね。

ネットワーク」はで表すことが多いです。ネットワークとは、いろんなものがつながっていることです。

ボク「ノード」ノ友達の「エッジ」くんを紹介します。

f:id:np2LKoo:20170903133955p:plain

エッジ

(ある高名な画家による肖像画

 

こんにちは。「エッジ」です。

ノード」とつながって

なにかを伝えるのが役目です。


点の部分のことを「ノード」、線の部分のことを「エッジ」といいます。

ノード」と「エッジ」でネットワークを表します。「ノード」をパソコンやルータ装置やハブ装置、「エッジ」を「LAN線」にみたてる魔法を使えば、「LANやクラウド」をあらわせます。人に見たてれてば、親子関係や血縁をあらわせそうです。つながりをもったあらゆるものを表します。

比較しよう

実際の「神経接続」と先輩たちが考えた「ニューラルネットワーク(NN)」とを比べてみましょう。

図  神経ネットワークとニューラルネットワーク(NN)

f:id:np2LKoo:20170903165838p:plain

神経接続

目の網膜(もうまく)にある視神経(ししんけい)の光を受けて反応する情報が、頭のうしろにある視覚野(しかくや)とよばれる部分にまでつながっているよ。

f:id:np2LKoo:20170903140716p:plain

ニューラルネットワーク(NN)

まあるい形の部分が「ノード

線でつながっている部分が「エッジ」です。

接続の形は似ていますが、神経細胞の「樹状突起」(じゅじょうとっき)や「軸索」(じくさく)に相当するものはありません。「神経」と「ノード」の「反応のしかた」も実はまったく違います。

にているところ

似ているところ 説明
接続のしかた

接続でよく(強く)つながっている部分と弱い部分がある。

神経細胞ではいくつもつながっていたり、よく反応する状態になります。

ニューラルネットワークは接続の強さを「重み(W)」ということもあります。

元気のでかた

しきい値があって、一定以上にならないと神経/ノードは元気にならない。

神経細胞では自律的に調整する部分もありますが、しきい値のようなところがあり、そこを超えれば、発火(はっか)する(興奮(こうふん)すること、元気になることです。もちろん火はでません)

ニューラルネットでは熱くなり方性格活性化化関数)にもよりますが、「バイアス(b)」で実質的なしきい値を調整します。しきい値といってもニューラルネットではゆるやかな場合が多いです。

案外にているところはすくないのかも。でも、接続の強さ(「重み」)としきい値(「バイアス」で調整)が似ていることはとっても重要です。

違うところ

違うところ     神経接続(しんけいせつぞく) ニューラルネットワーク(NN)
信号の伝わり方

神経はパルス信号(波形)が伝わります。こんな波です。(とがった波をパルスというよ)

f:id:np2LKoo:20170904213551j:plain

計算値が伝わります。計算時間以外は瞬間(しゅんかん)に伝わります。

時間について

神経は時間が関係ある

(波が最初から終わりまで伝わる時間や遅れがあります。)

時間が関係ない(考慮していない)

計算時間以外は遅れがない

活性化(かっせいか)

について

比較的極端(きょくたん)に元気になります。(活性化)

使う関数 f(性格)によりますが、

神経細胞とくらべ「なだらか」なものがつかわれることが多いです。

 

活性化(かっせいか)

の量について

(どれくらい元気か)

パルスの数や密度(みつど:こみぐあい)であらわします。 計算値が大きいか小さいかであらわします
学習のしかた まだまだ未解明(みかいめい)な部分が多いです。自己組織化(じこそしきか)と反省(フィードバック)と階層的最適化(どこをとってもエコ)かもしれません。「重ね合わせ」で同時に処理しているという先生もいます。

いろいろな方法が考えられています。誤差逆伝搬法(ごさぎゃくでんぱんほう)*1とよばれる方法など、たくさん考えられています。

接続 シナプスという部分で化学物質(かがくぶっしつ)をやりとりすることによりつたえます。まれに電気的につないでいる部分もあります。(普通の神経の接続は電気的な接続ではないです)

ノード」の層の間は全部「エッジ」でつながっている(層の考え方はあとからでてくるよ)

つなげたくない部分は「重み(w)」をゼロにする。(「重み(w)」もあとからでてくるよ)

部分とまとまり 入力の樹状突起から本体の細胞核(さいぼうかく)、軸索、出力のシナプスがワンセットです。

ノード」と「エッジ」からできる。

それぞれは別々にあつかう。

細かい部分はまだまだ違います。

ニューラルネットワーク(NN)

ニューラルネットワーク(NN)は情報を〇(ノード)と/(エッジ)の結合で学習し表現します。

では数字を学習(入力して圧縮して復元して出力)するニューラルネットワーク(NN)をつくってみましょう。
読み込む数字(画像が28×28で全部で784個あるとします)

f:id:np2LKoo:20170903180712p:plain

こんな図形を学習するよ。

つながりをあらわす材料

(1)784個の入力用〇(ノード
入力の画像をよみこむもの(目とかセンサーに相当するもの)

28×28の画像をもとにするけど、ドットを全部1列にならべます。画像の縦横のつながりはまったく関係なくなります。単に点が784個並んでいるだけです。(この点は重要です。点は画像でも、音の波(の点)、なんでもOKなんです)

f:id:np2LKoo:20170903212714p:plain

このノードを1列にならべてL0層(えるぜろそう)という仮の名前をつけます。(説明のためです)くどいですが、この時点でいったん、まわりのドットがどうなのかはまったく関係なくなります。順序は重要です。

(2)100個の中間〇ノード)(例では100個にしているけど、何個でもいいです。ただしゼロはダメです。)入力から読み込んだ情報をつたえてもらうものです。

f:id:np2LKoo:20170903213027p:plain

このノードを1列にならべてL1層(えるいちそう)という仮の名前をつけます。(説明のためです)

(3)784個の出力用〇ノード
画像を再現して出力するもの(再現された記憶や図形などに相当するもの)

f:id:np2LKoo:20170903213446p:plain

このノードを1列にならべてL2層(えるにそう)という仮の名前をつけます。(説明のためです)

画面に表示するときは784個ある点(ドット)を元の順序と同じにならべて28×28ドットの図形として表示することになります。

(4) (1)と(2)をつなぐ/エッジ
784×100本 (784個の入力〇(ノード)と100個の中間〇(ノード)をつなぐよ)

f:id:np2LKoo:20170903214056p:plain

(5) (2)と(3)をつなぐ/エッジ
100×784本 (100個の中間〇(ノード)と784個の出力〇(ノード)をつなぐよ)

f:id:np2LKoo:20170903221254p:plain

しくみのための材料

(6) /(エッジ)のつながりの強さをあらわす重み(W)の値(/(エッジ)の数ぶん)
784×100本 の/(エッジ)のW1
100×784本 の/(エッジ)のW2

1本の/(エッジ)に1つの「重み(W)」があります。「エッジのつながりがどれだけ強いか」をあらわすと思ってもらえばいいです。「ゼロ」のときは「つながっていないのとおなじこと」になります。

(7) 〇(ノード)の性格(f):熱くなりかた

1個の〇(ノード)に1つの「性格(f)」(せんもん的には活性化関数(f)(かっせいかかんすう)といいます。)
急に熱くなるか、ゆるやかに熱くなるかのような性格(f)を考えます。

f:id:np2LKoo:20170903232003p:plain

性格」という言い方はこのページだけの説明上のものです。(たとえです)
「ほどよく急な」ところと「なだからな」ところがある性格の形がよくつかわれます。

図にある「B」の部分はあとからでてくる「バイアス(B)」です。

性格(f):活性化関数(f)ってなに?

たとえばどんな性格(f)の形があるかというと
シグモイド関数(かんすう:
※なだらかーなところあれば急なところもあるほどよい性格(f)

f:id:np2LKoo:20170903232442p:plain

ReLU関数(らんぷ、れるー、あーるいーえるゆー、れくたふぁいあ すきに読んでいいよ:
※興味ないときはとことん全く興味ない性格(f)

f:id:np2LKoo:20170903233631p:plain

リニア関数(恒等関数:こうとうかんすう ともいいます:
※はいってきただけ熱くなるよ。わかりやすい性格(f)

f:id:np2LKoo:20170903233651p:plain

ほかにもたくさんあります。

(8)〇(ノード)のバイアス(B)
敏感(びんかん)なのか鈍感(どんかん)なのか「熱くなりやすさ」を調整(ちょうせい)する「バイアス(B)」というのをかんがえます。バイアスは単に「入力に加えて調整する」と理解するより、「入力に対してどう反応(はんのう)するのか」「グラフを横に動かして、調整する」と理解した方がわかりやすいです。※しきい値自体をかえるのではいことに注意しましょう。

f:id:np2LKoo:20170903231717p:plain
なお、入力層の〇(ノード)は元の画像がはいっているだけなので、「性格(f)」も「バイアス(B)」もありません。(必要ありません)

 

つないでみよう!

つなぎ方のお約束(ルール)

・いろんなつなぎ方が考えられるけど、今回は「〇」は1列(1層)に並んでいて、並んでいる横(列にならべたら縦)の「〇」とはどれも「/」でつながっていないことにします。
・ある列(L:層)の「〇」は次の列(L:層)の「〇」とは全部「/」でつながっているよ。
・ある列(L:層)にならんでいる「〇」はどれも同じ性格(f)と決めます。
・同じ性格(f)でも敏感なのか鈍感なのかが違い、それ(どらくらい敏感か鈍感か)をバイアス(B)で表します。
・「/」は「〇」から「〇」へ値を伝えますが、「重み(W)」を持っていてたくさん伝わるか、少ししか伝わらないかがかわります。「重み(W)」が大きいと前の〇の熱さの「値」がすくなくても、たくさん伝わるよ。
・それぞれの〇(ノード)は/(エッジ)で伝えられた値と性格(f)によってどれだけ熱くなったかの値を持ちます。

つないでみます

ノードはすべて〇であらわします。

エッジはすべて/であらわします。こんな感じになります。

f:id:np2LKoo:20170903210218p:plain

どんなふうに動くのかをみてみましょう。
まずは、全部1個づつで考えてみます

 

f:id:np2LKoo:20170906210958p:plain

何個かで考えてみます。

 

f:id:np2LKoo:20170906211012p:plain

な~んだ。「性格」はともかく、足し算と掛け算しかやってないじゃん。と思ってもらえればうれしいです。

数式って簡単に表すためのもの?伝えるためのもの?

でも掛け算や足し算を何百個、何千回もやるのはやだ!めんどくさい!!
でも昔のめんどぐさがり屋さん(数学者:すうがくしゃとも言います)が簡単にする方法を考えだしました。
ぱっぱら~♪「全部いっぺんにまとめて計算する方法」です。
とにかくまとめて1つにして考える方法です。

 

入力(L0:えるぜろ層)のノードの値「X」

入力は784個ありましたが、これをまとめて1個であらわす方法は
(えっくす)
です。の中には784個の数字がはいっています。イメージしてみましょう。
ちゃんとならんで入っています。1列に並んで入って入っています。
これを784個ある入力□(ノード)にそのままセットします。
一列の箱の区切り中に数字がはいったものをイメージしてくれたお友達も多いかもしれません。
袋の中に784個のクッキーがはいっているのを想像したおともだちもいるかもしれません。
本棚の中に784冊の本がならんでいるのを思った人もいるかもしれません。
全部OKです。だた、どれも中のものには番号がついていてわかるようになっています。
ひとまとめてしまえば、「1個」で扱えます。「1箱」「1袋」「1本棚」です。
数字や記号だけであらわすと次のようになります。

入力エックス

エッジの重み「W」

次に/(エッジ)の部分を考えてみましょう
入力(L0:えるぜろ層)と中間(L1:えるいち層)のつながりは
784×100本
でした。
入力の〇(ノード)が784個で、中間の〇(ノード)を100個にしているから全部線をひくと掛け算になるよね。
(784個ある入力〇ノードからはそれぞれ100本線が引けるから...78,600本だ!)
昔のめんどくさがり屋さん(数学者:すうがくしゃとも言います)はもちろんこれも簡単に1つで扱う方法を考えました。
1個であらわす方法は
(だぶりゅー)
です。の中には784個の行とそれぞれ100個の列があります。
箱の中に784行の横の仕切りがあって、その行の中に100個の縦の仕切りがあって数字がはいっているのをイメージしてくれたお友達も多いかもしれません。
袋の中に784個の小袋がはいっていてその中に100個つづクッキーがはいっているのを想像したおともだちもいるかもしれません。
図書館の中に本棚が784棚あって、そのそれぞれの中に100冊の本がならんでいるのを思った人もいるかもしれません。
全部OKです。だた、どれも中のものには番号がついていてわかるようになっています。
「何行目、何列目」とか「袋番号、その中のクッキー番号(何個目か)」とか「棚番号、そのなかの本番号(何冊目か)」です。
このように中にはいっているものを2つの数字であらわすことができます。(これを数学では2次元行列といいます。)

重みダブリュー

中間ノード(L1:えるいち層)の値「Z」

次に中間の〇(ノード)をかんがえましょう。
100個あります。この〇(ノード)にはいってくる(伝わる)値を考えます。

上で出てきた、で考えてみましょう。
伝わる値
×   です。  
エックスと掛け算がわかりにくですね。数学ではエックスをよく使います。掛け算もよく使います。
昔のめんどぐさがり屋さん(数学者:すうがくしゃとも言います)は大発明をしました。
掛け算はよく使うし、いちいち書くのはめんどくさいから、省略しちゃおう!つづて書いたら掛け算したことにしちゃおう!!
こうして人類(じんるい)は「掛け算を省略して書く術(じゅつ)」を習得してレベルアップしました!!!
伝わる値は

と書けます。
これで中間ノード100個分(ということはエッジ784×100本ぶん)の計算を一度に全部考えたことになります!(だって、は784個分、は784×100個分なので!)おお、行列ってちょー便利。エコです。

でははいってきた全部の値「」でそれぞれの中間の〇(ノード)はどれくらいの値になるのでしょうか
数式であらわすと便利なので、昔のめんどくさがり屋さん(数学者:すうがくしゃとも言います)がこんなときどうあわらしたか見てみましょう。
はいってきた値
(ぜっと)は〇(ノード)がどれだけ熱くなったかの値です。
は活性化関数といいます。上の説明では「性格」として説明していたものです。
でてくる値=(入れる値)
のようにあらわします。こんな風に昔のめんどくさがり屋さん(数学者:すうがくしゃとも言います)は計算の方法を簡単(かんたん)に表す表し方をくふうして簡単にできるしくみを考えてきました。
一見(いっけん)めんどくさそうな表し方でも組み合わせたり、応用(おうよう)したりすると実はそれが「簡単」であることがたくさんあります。それに数式は「ことば」でもあり、「あらわし方の全世界共通(ぜんせかいきょうつう)のルール」でもあります。
これを覚(おぼ)えれば外国の人と「数学」で会話ができます。(数式は他の人に考えを伝える共通の言葉であり、また書物(しょもつ)の文書(ぶんしょ)のように後世(こうせい)にもそれを伝えていくものです。そしてなにより自分の考えをまとめるのに便利です)*2
どれくらい笑うか=わらう関数(おもしろいこと)
おもしろいことを言ってどれくらい笑うのかの「性格:関数」をかんがえみましょう。
「わらわない」=わらう関数(「けしゴムころがった」)
「にやけた」=わらう関数(「けしゴムころがった」+「ふとんがふっとんだ」)
「爆笑」=わらう関数(「けしゴムころがった」+「ふとんがふっとんだ」+「ぶどうひとつぶどう」)

でも、ひとによって笑い上戸(じょうご)のひとと、リアクションが薄(うす)い人がいます。
そんなとき、調整をするのがバイアス(B)、別名「下駄(げた)」とかいいます。縦方向に使えば背が高くなります。横方向に使えば、グラフの形が横に移動(シフト)します。
バイアスを使って、笑い上戸の人とリアクションが薄い人を表してみましょう。
笑い上戸の人
(図を書く)

リアクションが薄い人
(図を書く)

問題:「けしゴムころがった」だけで笑う人はどんなバイアスを持った人でしょう。(バイアスはプラス?マイナス?)

100個の〇(ノード)がどれだけ熱くなるか(活性化するか)は下の式であらわされます。

ここでBはそれぞれの〇(ノード)のバイアスです。バイアスは入力ではありません!それぞれの〇(ノード)で固有にもっている関数のはたらき具合を調整するものです。重さでもありません。また、/(エッジ)が持っている値ではありません!〇(ノード)が固有にもっている(そして〇(ノード)のもつ関数にくっついている)値です。ここは特に勘違(かんちが)いしやすいので注意しましょう。
急に難(むずか)しくなった気がする人もいるかもしれませんが、表し方をかえただけです。昔のめんどくさがりな人がわざわざ難しくしていることはないんです。要(よう)は慣(な)れです。言葉であらわすとこんな感じです。

それぞれの人がどれだけ笑うか=わらう関数(ぜんぶのおもしろ話(のおもしろさ)+それぞれの人の笑いやすさを調整する値)

つぎのエッジの重み「W2」

次に中間(L1:層)から出力(L2:層)への重みW2を考えましょう。(最初のW1にしちゃいましょう)
ちょっとした「発想(はっそう)の転換(てんかん)」(ちがったみかたをすること)が必要です。
どうやったら少ない100個の値から元の784の値を復元できるのでしょうか?
普通に考えると100個になってしまった値が784個になることはありません。
でもみんなで力をあわせればできるかもしれません。きっとできるはずです!

ちょっとするします。カンニングともいいます。
入力(L0:えるぜろ)層を中間(L1:えるいち層)で折り返して出力(L2:えるに層)にします。
そうすれば一旦中間で少なくなったノード「数」をもとのノードの「数」に戻して、元のノードの中にはいっていた値を復元できるかもしれません。ここで、「カンニング」(ずる)といっても元の値をそのままコピペするのではなく、つながり方や重みをコピペします。実はこうすることによって(で)も、ほどよく元にもどせることがあります。

やってみましょう。(かんにんぐ)
W1W2にコピペ。。。できません。
W1は784×100のエッジの重み
W2は100×784のエッジの重みで総数は同じですが、形が違います。
どうすればいいのでしょうか。むりやりコピペする方法はないでしょうか。

じつは線対称でひっくりかえす考えなので、W1をひっくりかえして複写すればいいのです。
W1をひっくりかえす?そうです。行と列をひっくりかえします。
そうすれば、W1は784行100列から100列784行になるので、W2にばっちり「はめこむ」ことができます。
この行と列をひくりかえすことを転置(てんち)といいます。
W1をひっくりかえして書くのはむずかしいのでにするとわからなくなるので、 { W1^{\mathrm{T}} }と書きます。例をみてみましょう
(例を書く)

とりあえずここでは
W2W1

としておきます。これを専門的には「重み共有(きょうゆう)」といいます
まぁ、ちょっとしたずる(手抜き:てぬき)です。

出力(L2:えるに層)の値「Y」

さて、最後の層になりました。出力(L2:えるに層)をみてみましょう。
前の中間層の〇(ノード)の値「」を使って計算してみましょう。
出力はY(わい)を使ってみます。

W2B2
W2B2は別に2をかけているわけではなくて、2ばんめ(の部分)という意味です。「W2」で1文字のように考えてください。W2B2も例によって「ぜんぶをいっぺんにあらわす」行列を使っているよ。B2は出力(L2:えるに層)のノード768個あるので、B2の行数も768個あるよ。中間層用のB1(中身は100個)とB2(中身は784個)は折り返し(重み共有)で複写できません。(最初のB2もでてきたので、WでやったようにB1とあらわします。)
重みはひっくり返して(転置(てんち)して)使うけど、バイアスは共有しません。だから「重み共有」で「バイアス」は関係ないのです。

これで、入力784個から100個に圧縮して、784個に戻しました。
たとえば入力784個から100個にすること、符号化(ふごうか:エンコード:べつの形であらわすこと)
100個から784個へ出力することを、復号化(ふくごうか:デコード:元の形にもどすこと)といいます。

これで使えるの?

さてこれで本当にいったん別の形であらわしたものがもとに戻るのでしょうか。
じつは、まだもとに戻せないのです。の値をいい値にきめないといけません。

苦労してやってきましたが、まだゴールは遠いのです。
次のことが必要です。
・図形などをちゃんと戻せるように「W1W2の値を決める」
・図形などちゃんと戻せるように「B1B2の値を決める」
なんだ4つじゃん。あれ?
W1W2はそれぞれ784×100=78400個、100×784=78400個
B1B2はそれぞれ100個と784個あります。
けどW2は「ずる」(エコ、効率化)してW1をひっくりかえして複写するのでW1の78400個
だから合計79,268個の値を決めないといけません。4個じゃなかった!

えっ!?まだ続くの?

どうやってきめればいいのでしょうか。次は「教師:きょうし(せんせい)」という考えかたがでてきます
もう少しがんばってみたいお友達は次の「ノードは気長に学習していく編」をみてくだい。乞うご期待(するほどではないです)

(参考)

行列をかけるときはきまりがあります。
前のほうの行と次の列が「同じ数」そろっている必要があります。つながる相棒がいないと計算できません(つながりません)
入力〇     /エッジ     中間〇   /エッジ    出力

f:id:np2LKoo:20170906214840p:plain

「行列」のことは「イメージ」しか書かなかったけど、高校の数学でならいます。
たのしみにしていてね。

(追記:注意)すみません、高校ではいまは「行列」ないそうです。そのかわり複素数(ふくそすう)をたくさんならうようです。え~ほんとかな?ボクハ行列、習いましたよ?理系?の高校ではならうのかな?いやぁ、日本がAI(えーあい:じんこうちのう)がんばるんなら、まず「行列」を復活(ふっかつ)させないとダメでしょ。VRで3Dもさかんなので、3D計算するなら必須(ひっす:かならずひつようなこと)ですし。(でも複素数もとっても大事ですね。電磁気学(でんじきがく)とか量子力学(りょうしりきがく)やるならこちらですね。波を扱ったとたん複素数が大活躍(だいかつやく)です。)こんど、文部科学省(もんぶかがくしょう)に高校で「行列」を復活させるようメールしておきます。(ウソで~す。)機械学習(きかいがくしゅう)を「本気(マジ)」で勉強したいお友達は大学のおにーさん、おねーさんに教えてもらうか、大学までお待ちください。まぁ、そういうボクモ「コレ習っても絶対、一生使うことはない」と思っていたのを告白(こくはく:「愛してます」ではなく「ゲロ」の方)します。今は、毎日コレばっかりです。(追記:ここまで)

最後までよんでくれて本当にありがとう

実は本当によんでくれている「おたまじゃくし」や「小中学校のお友達」がとっても少ない気配(けはい)を感じています。(書いている「かえる」や「人」は、どんな人が読んでくれているのか正確(せいかく)にはわかりませんが「傾向」(けいこう:だいたいのかんじ)はなんとなくわかります)でも1人でも、「最後まで読んでもらえるお友達がきっといる」と信じて次もがんばります。

*1:誤差逆伝搬法(ごさぎゃくでんぱんほう):計算値と教師の値(ほんとうに欲しかった値)の最終的な差を遡(さかのぼ)って、各部分が学習(調整)していきます。

*2:ほかにも「音楽」でも会話(かいわ)ができます。「見知らぬ外国の人が言葉も通じないのに空港(くうこう)にたまたまあったピアノで、いきなり打ち合わせもせず完全にアドリブで連弾(れんだん)セッション(がっそう)する」なんていうことができます。2人とも「音楽」を受信(じゅしん:みみできいて)できて発信(はっしん:手でえんそう)できたからです。たとえばこんなのとか。コミュニケーション(キャッチボール)だというのがよくわかります