# -*- coding: UTF-8 -*- import sys from math import * from random import * import numpy as np ###################################################### # バックプロパゲーションの制御(クラス BackControl) ###################################################### class BackControl : #################################### # クラスBackControlのコンストラクタ # name : 入力データファイル名 #################################### def __init__(self, name) : f = open(name, "r") s = f.readline().split() self.eps = float(s[1]) # 許容誤差 self.p_type = int(s[3]) # 出力先・方法の指定 # =0 : 誤って認識した数だけ出力 # =1 : 認識結果を出力 # =2 : 認識結果と重みを出力 # (負の時は,認識結果と重みをファイルへも出力) if self.p_type < 0 : self.o_file = s[5] # 出力ファイル名 s = f.readline().split() self.order = int(s[1]) # 入力パターンの与え方(=0:順番,=1:ランダム) self.eata = float(s[3]) # 重み及びバイアス修正パラメータ self.alpha = float(s[5]) # 重み及びバイアス修正パラメータ f.close() ###################################################### # バックプロパゲーションのデータ(クラス BackData) ###################################################### class BackData : #################################### # クラスBackDataのコンストラクタ # name : 入力データファイル名 #################################### def __init__(self, name) : # 入力パターン数等 f = open(name, "r") s = f.readline().split() self.noip = int(s[1]) # 入力パターンの数 self.noiu = int(s[3]) # 入力ユニットの数 self.noou = int(s[5]) # 出力ユニットの数 # 領域の確保 self.iptn = np.empty((self.noip, self.noiu), np.float) # iptn[i][j] : (i+1)番目の入力パターンの(j+1)番目の # 入力ユニットの入力値 # i=0,noip-1 j=0,noiu-1 self.optn = np.empty((self.noip, self.noou), np.float) # optn[i][j] : (i+1)番目の入力パターンに対する(j+1) # 番目の出力ユニットの目標出力値 # i=0,noip-1 j=0,noou-1 # 入力パターン及び各入力パターンに対する出力パターンの入力 for i1 in range(0, self.noip) : s = f.readline().split() for i2 in range(0, self.noiu) : self.iptn[i1][i2] = float(s[i2+1]) s = f.readline().split() for i2 in range(0, self.noou) : self.optn[i1][i2] = float(s[i2+1]) f.close() ########################################### # バックプロパゲーション(クラス Backpr) ########################################### class Backpr(BackControl) : ################################################ # クラスBackprのコンストラクタ # name_c : 制御データ用入力ファイル名 # name_s : ネットワーク記述入力ファイル名 ################################################ def __init__(self, name_c, name_s) : BackControl.__init__(self, name_c) # 親のコンストラクタ f = open(name_s, "r") # 入力ユニット,出力ユニットの数,関数タイプ s = f.readline().split() self.noiu = int(s[1]) # 入力ユニットの数 self.noou = int(s[3]) # 出力ユニットの数 self.f_type = int(s[5]) # シグモイド関数のタイプ,0 : [0,1],1 : [-1,1] self.nou = self.noiu + self.noou # 入力ユニットと出力ユニットの和 # 各ユニットには最も上の出力ユニットから, # 隠れ層の各ユニット,及び,入力ユニットに至る # 一連のユニット番号が付けられる # 隠れユニットの階層数と各階層のユニット数 s = f.readline().split() self.nolvl = int(s[1]) # 隠れユニットの階層数 self.nohu = np.empty(self.nolvl+1, np.int) # nohu[i] : レベル(i+1)の隠れ層のユニット数(隠れ層 # には下から順に番号が付けられ,出力層はレ # ベル(nolvl+1)の隠れ層とも見做される) # i=0,nolvl self.nohu[self.nolvl] = self.noou if self.nolvl > 0 : for i1 in range(0, self.nolvl) : self.nohu[i1] = int(s[i1+3]) self.nou += self.nohu[i1] # 領域の確保 self.con = np.empty((self.nou, self.nou), np.int) for i1 in range(0, self.nou) : for i2 in range(0, self.nou) : if i1 == i2 : self.con[i1][i2] = -1 else : self.con[i1][i2] = 0 # con[i][j] : 各ユニットに対するバイアスの与え方,及び,接続方法 # [i][i] : ユニット(i+1)のバイアスの与え方 # =-3 : 入力値で固定 # =-2 : 入力値を初期値として,学習により変更 # =-1 : 乱数で初期値を設定し,学習により変更 # [i][j] : ユニット(i+1)と(j+1)の接続方法(j>i) # =0 : 接続しない # =1 : 接続する(重みの初期値を乱数で設定し,学習) # =2 : 接続する(重みの初期値を入力で与え,学習) # =3 : 接続する(重みを入力値に固定) # i=0,nou-1 j=0,nou-1 self.w = np.empty((self.nou, self.nou), np.float) for i1 in range(0, self.nou) : for i2 in range(0, self.nou) : self.w[i1][i2] = 0.0 # w[i][j] : ユニット(i+1)から(j+1)への重み(j>i) # w[j][i] : (i+1)から(j+1)への重みの前回修正量(j>i) # w[i][i] : ユニット(i+1)のバイアスの前回修正量 # i=0,nou-1 j=0,nou-1 self.dp = np.empty(self.nou, np.float) # ユニット(i+1)の誤差 i=0,nou-1 self.op = np.empty(self.nou, np.float) # ユニット(i+1)の出力 i=0,nou-1 self.theta = np.empty(self.nou, np.float) # ユニット(i+1)のバイアス i=0,nou for i1 in range(0, self.nou) : self.theta[i1] = 0.2 * random() - 0.1 # 各ユニットのバイアスとユニット間の接続関係 # バイアス # バイアスデータの数 s = f.readline().split() n = int(s[1]) f.readline() f.readline() f.readline() if n > 0 : # バイアスデータの処理 for i0 in range(0, n) : s = f.readline().split() # ユニット番号 k1 = int(s[0]) # 不適当なユニット番号のチェック if k1 < 1 or k1 > (self.nou-self.noiu) : print("***error ユニット番号 " + str(k1) + " が不適当") # バイアスの与え方 k1 -= 1 id = int(s[1]) self.con[k1][k1] = id # バイアスの初期設定 if self.con[k1][k1] == -1 : x1 = float(s[2]) x2 = float(s[3]) self.theta[k1] = (x2 - x1) * random() + x1 elif self.con[k1][k1] == -2 or self.con[k1][k1] == -3 : self.theta[k1] = float(s[2]) else : print("***error バイアスの与え方が不適当") # 接続方法 # 接続データの数 s = f.readline().split() n = int(s[1]) f.readline() f.readline() f.readline() if n > 0 : # 接続データの処理 for i0 in range(0, n) : s = f.readline().split() # 接続情報 k1 = int(s[0]) k2 = int(s[1]) k3 = int(s[2]) k4 = int(s[3]) # 不適切な接続のチェック sw = 0 if k1 < 1 or k2 < 1 or k3 < 1 or k4 < 1 : sw = 1 else : if k1 > self.nou or k2 > self.nou or k3 > self.nou or k4 > self.nou : sw = 1 else : if k1 > k2 or k3 > k4 : sw = 1 else : if k4 >= k1 : sw = 1 else : l1 = -1 k = 0 for i1 in range(self.nolvl, -1, -1) : k += self.nohu[i1] if k1 <= k : l1 = i1 break l2 = -1 k = 0 for i1 in range(self.nolvl, -1, -1) : k += self.nohu[i1] if k4 <= k : l2 = i1 break if l2 <= l1 : sw = 1 if sw > 0 : print("***error ユニット番号が不適当(" + str(k1) + " " + str(k2) + " " + str(k3) + " " + str(k4) + ")") # 重みの初期値の与え方 k1 -= 1 k2 -= 1 k3 -= 1 k4 -= 1 id = int(s[4]) if id == 1 : x1 = float(s[5]) x2 = float(s[6]) else : if id > 1 : x1 = float(s[5]) else : if id != 0 : print("***error 接続方法が不適当") # 重みの初期値の設定 for i1 in range(k3, k4+1) : for i2 in range(k1, k2+1) : self.con[i1][i2] = id if id == 0 : self.w[i1][i2] = 0.0 elif id == 1 : self.w[i1][i2] = (x2 - x1) * random() + x1 elif id == 2 : self.w[i1][i2] = x1 elif id == 3 : self.w[i1][i2] = x1 f.close() ########################################## # 誤差の計算,及び,重みとバイアスの修正 # ptn[i1] : 出力パターン ########################################## def Err_back(self, ptn) : for i1 in range(0, self.nou-self.noiu) : # 誤差の計算 if i1 < self.noou : if self.f_type == 0 : self.dp[i1] = (ptn[i1] - self.op[i1]) * self.op[i1] * (1.0 - self.op[i1]) else : self.dp[i1] = 0.5 * (ptn[i1] - self.op[i1]) * (self.op[i1] - 1.0) * (self.op[i1] + 1.0) else : x1 = 0.0 for i2 in range(0, i1) : if self.con[i2][i1] > 0 : x1 += self.dp[i2] * self.w[i2][i1] if self.f_type == 0 : self.dp[i1] = self.op[i1] * (1.0 - self.op[i1]) * x1 else : self.dp[i1] = 0.5 * (self.op[i1] - 1.0) * (self.op[i1] + 1.0) * x1 # 重みの修正 for i2 in range(i1+1, self.nou) : if self.con[i1][i2] == 1 or self.con[i1][i2] == 2 : x1 = self.eata * self.dp[i1] * self.op[i2] + self.alpha * self.w[i2][i1] self.w[i2][i1] = x1 self.w[i1][i2] += x1 # バイアスの修正 if self.con[i1][i1] >= -2 : x1 = self.eata * self.dp[i1] + self.alpha * self.w[i1][i1] self.w[i1][i1] = x1 self.theta[i1] += x1 ######################################################## # 与えられた入力パターンに対する各ユニットの出力の計算 ######################################################## def Forward(self) : for i1 in range(self.nou-self.noiu-1, -1, -1) : sum = -self.theta[i1] for i2 in range(i1+1, self.nou) : if self.con[i1][i2] > 0 : sum -= self.w[i1][i2] * self.op[i2] if self.f_type == 0 : self.op[i1] = 1.0 / (1.0 + exp(sum)) else : self.op[i1] = 1.0 - 2.0 / (1.0 + exp(sum)) ############################# # 学習の実行 # p : 認識パターン # m_tri : 最大学習回数 ############################# def Learn(self, p, m_tri) : k0 = -1 # エラーチェック if self.noiu != p.noiu or self.noou != p.noou : print("***error 入力または出力ユニットの数が違います") for i1 in range(0, m_tri) : # パターンを与える順番の決定 if self.order == 0 : # 順番 k0 += 1 if k0 >= p.noip : k0 = 0 else : # ランダム k0 = int(random() * p.noip) if k0 >= p.noip : k0 = p.noip - 1 # 出力ユニットの結果を計算 k1 = self.nou - self.noiu for i2 in range(0, self.noiu) : self.op[k1+i2] = p.iptn[k0][i2] self.Forward() # 重みとバイアスの修正 self.Err_back(p.optn[k0]) ################################################ # 与えられた対象の認識と出力 # p : 認識パターン # pr : =0 : 出力を行わない # =1 : 出力を行う # =2 : 出力を行う(未学習パターン) # tri : 現在の学習回数 # return : 誤って認識したパターンの数 ################################################ def Recog(self, p, pr, tri) : No = 0 # ファイルのオープン if self.p_type < 0 and pr > 0 : if pr == 1 : out = open(self.o_file, "w") out.write("***学習パターン***\n\n") else : out = open(self.o_file, "a") out.write("\n***未学習パターン***\n\n") # 各パターンに対する出力 for i1 in range(0, p.noip) : # 入力パターンの設定 k1 = self.nou - self.noiu for i2 in range(0, self.noiu) : self.op[k1+i2] = p.iptn[i1][i2] # 出力の計算 self.Forward() # 結果の表示 if self.p_type != 0 and pr > 0 : print("入力パターン{0:4d} ".format(i1+1), end="") for i2 in range(0, self.noiu) : print("{0:5.2f}".format(self.op[k1+i2]), end="") if i2 == self.noiu-1 : print("") else : if ((i2+1) % 10) == 0 : print("\n ", end="") print("\n 出力パターン(理想) ", end="") for i2 in range(0, self.noou) : print("{0:10.3f}".format(p.optn[i1][i2]), end="") if i2 == self.noou-1 : print("") else : if ((i2+1) % 5) == 0 : print("\n ", end="") sw = 0 if self.p_type != 0 and pr > 0 : print(" (実際) ", end="") for i2 in range(0, self.noou) : if self.p_type != 0 and pr > 0 : print("{0:10.3f}".format(self.op[i2]), end="") if i2 == self.noou-1 : print("\n") else : if ((i2+1) % 5) == 0 : print("\n ", end="") if abs(self.op[i2]-p.optn[i1][i2]) > self.eps : sw = 1 if sw > 0 : No += 1 if self.p_type < 0 and pr > 0 : out.write("入力パターン{0:4d} ".format(i1+1)) for i2 in range(0, self.noiu) : out.write("{0:5.2f}".format(self.op[k1+i2])) if i2 == self.noiu-1 : out.write("\n") else : if ((i2+1) % 10) == 0 : out.write("\n ") out.write("\n 出力パターン(理想) ") for i2 in range(0, self.noou) : out.write("{0:10.3f}".format(p.optn[i1][i2])) if i2 == self.noou-1 : out.write("\n") else : if ((i2+1) % 5) == 0 : out.write("\n ") out.write(" (実際) ") for i2 in range(0, self.noou) : out.write("{0:10.3f}".format(self.op[i2])) if i2 == self.noou-1 : out.write("\n") else : if ((i2+1) % 5) == 0 : out.write("\n ") if self.p_type != 0 and pr > 0 : input("") # 重みの出力 if (self.p_type < -1 or self.p_type > 1) and pr == 1 : print(" 重み") for i1 in range(0, self.nou-self.noiu) : print(" to{0:4d} from ".format(i1+1), end="") ln = -1 for i2 in range(0, self.nou) : if self.con[i1][i2] > 0 : if ln <= 0 : if ln < 0 : ln = 0 else : print("\n ", end="") print("{0:4d}{1:11.3f}".format(i2+1, self.w[i1][i2]), end="") ln += 1 if ln == 4 : ln = 0 print("") print("\n バイアス ", end="") ln = 0 for i1 in range(0, self.nou-self.noiu) : print("{0:4d}{1:11.3f}".format(i1+1, self.theta[i1]), end="") ln += 1 if ln == 4 and i1 != self.nou-self.noiu-1 : ln = 0 print("\n ", end="") print("") input("") if self.p_type < 0 and pr == 1 : out.write(" 重み\n") for i1 in range(0, self.nou-self.noiu) : out.write(" to{0:4d} from ".format(i1+1)) ln = -1 for i2 in range(0, self.nou) : if self.con[i1][i2] > 0 : if ln <= 0 : if ln < 0 : ln = 0 else : out.write("\n ") out.write("{0:4d}{1:11.3f}".format(i2+1, self.w[i1][i2])) ln += 1 if ln == 4 : ln = 0 out.write("\n") out.write("\n バイアス ") ln = 0 for i1 in range(0, self.nou-self.noiu) : out.write("{0:4d}{1:11.3f}".format(i1+1, self.theta[i1])) ln += 1 if ln == 4 and i1 != self.nou-self.noiu-1 : ln = 0 out.write("\n ") if ln != 0 : out.write("\n") if self.p_type < 0 and pr > 0 : out.close() return No ---------------------------------- # -*- coding: UTF-8 -*- import numpy as np import sys from math import * from random import * from function import Backpr, BackData, BackControl ########################## # back propagation model # coded by Y.Suganuma ########################## ct = 0 no = 1 # エラー if len(sys.argv) != 3 : print("***error 入力データファイル名を指定して下さい") else : # ネットワークの定義 net = Backpr(sys.argv[1], sys.argv[2]) # 学習パターン等の入力 m_tri = int(input("学習回数は? ")) conv = int(input("何回毎に収束を確認しますか? ")) f_name = input("学習パターンのファイル名は? ") dt1 = BackData(f_name) # 学習 while ct < m_tri and no > 0 : if (ct + conv) < m_tri : tri = conv else : tri = m_tri - ct ct += tri net.Learn(dt1, tri) # 学習 no = net.Recog(dt1, 0, ct) # 学習対象の認識 print(" 回数 " + str(ct) + " 誤って認識したパターン数 " + str(no)) no = net.Recog(dt1, 1, ct) # 学習対象の認識と出力 # 未学習パターンの認識 sw = int(input("未学習パターンの認識を行いますか?(=1:行う,=0:行わない) ")) if sw > 0 : f_name = input("未学習パターンのファイル名は? ") dt2 = BackData(f_name) no = net.Recog(dt2, 2, ct) # 未学習対象の認識と出力 ------------------------制御データ---------------- 誤差 0.1 出力 -2 出力ファイル kekka 順番 0 η 0.5 α 0.8 ------------------------構造データ---------------- 入力ユニット数 2 出力ユニット数 1 関数タイプ 0 隠れ層の数 1 各隠れ層のユニット数(下から) 1 バイアス入力ユニット数 1 ユニット番号:出力ユニットから順に番号付け 入力方法:=-3:固定,=-2:入力後学習,=-1:乱数(default,[-0.1,0.1])) 値:バイアス値(ー2またはー3の時)または一様乱数の範囲(下限,上限) 1 -1 -0.05 0.05 接続方法の数 2 ユニット番号:ユニットk1からk2を,k3からk4に接続 接続方法:=0:接続なし,=1:乱数,=2:重み入力後学習,=3:重み固定 値:重み(2または3の時)または一様乱数の範囲(1の時:下限,上限) 3 4 1 2 1 -0.1 0.1 2 2 1 1 1 -0.1 0.1 ------------------------学習データ---------------- パターンの数 4 入力ユニット数 2 出力ユニット数 1 入力1 0 0 出力1 0 入力2 0 1 出力2 1 入力3 1 0 出力3 1 入力4 1 1 出力4 0 ------------------------認識データ---------------- パターンの数 4 入力ユニット数 2 出力ユニット数 1 入力1 0 0 出力1 0 入力2 0 1 出力2 1 入力3 1 0 出力3 1 入力4 1 1 出力4 0