# -*- 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