|
|
# coding = UTF-8
|
|
|
import os
|
|
|
import sys
|
|
|
import pickle
|
|
|
import subprocess
|
|
|
import functools
|
|
|
import numpy as np
|
|
|
import pandas as pd
|
|
|
import tkinter as tk
|
|
|
from tkinter import ttk
|
|
|
from tkinter import messagebox
|
|
|
import tkinter.scrolledtext as st
|
|
|
from PIL import Image, ImageTk
|
|
|
from tkinter import Canvas, Frame, BOTH, PhotoImage, filedialog
|
|
|
from X3 import setload_data,load_data
|
|
|
from X2 import ModelObj
|
|
|
# 网络模型正确的图元和连接关系的数据结构,用一个字典表示
|
|
|
RIGHT_MODEL = [
|
|
|
[0, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
|
[0, 0, 1, 0, 0, 0, 0, 0, 0],
|
|
|
[0, 1, 0, 1, 0, 0, 0, 0, 0],
|
|
|
[0, 0, 0, 1, 1, 0, 0, 0, 0],
|
|
|
[0, 0, 0, 0, 0, 1, 0, 0, 0],
|
|
|
[0, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
|
[0, 0, 0, 0, 0, 0, 0, 1, 1],
|
|
|
[0, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0]
|
|
|
]
|
|
|
|
|
|
# 创建三个空的卷积层图元实例和三个空的全连接层图元实例
|
|
|
Conv1 = None
|
|
|
Conv2 = None
|
|
|
Conv3 = None
|
|
|
Pool1 = None
|
|
|
Pool2 = None
|
|
|
Pool3 = None
|
|
|
Fullconn1 = None
|
|
|
Fullconn2 = None
|
|
|
Fullconn3 = None
|
|
|
|
|
|
global Viewcanvas # 定义画布
|
|
|
global Text # 带滚动条
|
|
|
global Root # 主窗口
|
|
|
global AllModelObj #网络对象
|
|
|
|
|
|
List_image = [] # 存储图元的列表
|
|
|
Prob_images = [] # 存储图元的列表
|
|
|
Train_images = [] # 存储训练集的列表
|
|
|
Test_images = [] # 存储测试集的列表
|
|
|
Items1 = [] # 存储新图元的列表
|
|
|
Items2 = [] # 存储新数据线的列表
|
|
|
Items3 = [] # 存储被拖拽的图元的列表
|
|
|
Items4 = [] # 存储新图元文字标签的列表
|
|
|
Counts = {} # 用来存储Items3每个数字出现过的次数
|
|
|
Conv_xy_list = [] # 存储卷积图元的坐标
|
|
|
Pool_xy_list = [] # 存储全连接图元的坐标
|
|
|
Fullconn_xy_list = [] # 存储全连接图元的坐标
|
|
|
Train_list = [] # 存储训练设置参数
|
|
|
Listinstance = [] # 图元对象实例
|
|
|
Dragged_item = () # 记录被拖拽的图元id
|
|
|
Original_x = 0 # 记录图元原来的x坐标
|
|
|
Original_y = 0 # 记录图元原来的y坐标
|
|
|
Offset_x = 0 # 记录鼠标相对于图元的x偏移量
|
|
|
Offset_y = 0 # 记录鼠标相对于图元的y偏移量
|
|
|
Yt = 0 # 新图元的y坐标
|
|
|
Batch = 0 #批次
|
|
|
|
|
|
def create_instance():
|
|
|
# 创建图元对象实例
|
|
|
DataSet = ModelObj("DataSet", 1, "数据集", "LoadData", "SetDataPara", ".", 90, 40).output()
|
|
|
Conv = ModelObj("Conv", 2, "卷积", "ConvProc", "SetConvPara", ".", 200, 40).output()
|
|
|
Pool = ModelObj("Pool", 3, "最大池化", "MaxPoolProc", "SetPollPara", ".", 90, 105).output()
|
|
|
FullConn = ModelObj("FullConn", 4, "全连接", "FullConnProc", "SetFullConnPara", ".", 200, 105).output()
|
|
|
Nonline = ModelObj("Nonline", 5, "非线性函数", "NonlinearProc", "SetNonLPara", ".", 90, 170).output()
|
|
|
Classifier = ModelObj("Classifier", 6, "分类", "ClassifierProc", "SetClassifyPara", ".", 200, 170).output()
|
|
|
Error = ModelObj("Error", 7, "误差计算", "ErrorProc", "SetErrorPara", ".", 90, 235).output()
|
|
|
AjConv = ModelObj("AjConv", 8, "卷积调整", "AjConvProc", "SetAjConvPara", ".", 200, 235).output()
|
|
|
AjFullconn = ModelObj("AjFullconn", 9, "全连接调整", "AjFullconnProc", "SetAjFCPara", ".", 90, 300).output()
|
|
|
AjNonline = ModelObj("AjNonline", 10, "非线性调整", "AjNonlineProc", "SetAjNLPara", ".", 200, 300).output()
|
|
|
listinstance = [DataSet, Conv, Pool, FullConn, Nonline, Classifier, Error, AjConv, AjFullconn, AjNonline]
|
|
|
return listinstance # 还回图元对象实例列表
|
|
|
|
|
|
def picture_frame(window_width,window_height):
|
|
|
gap = 20 # 设置矩形框之间的间隔为10像素
|
|
|
# 计算矩形框的坐标和尺寸
|
|
|
rect1_x = gap # 第一个矩形框的左上角x坐标
|
|
|
rect1_y = gap # 第一个矩形框的左上角y坐标
|
|
|
rect1_width = window_width * 1 / 3 - gap-30 # 第一个矩形框的宽度
|
|
|
rect1_height = window_height * 3 / 4 - gap * 2 # 第一个矩形框的高度
|
|
|
rect2_x = window_width * 1 / 3 + gap - 30 # 第二个矩形框的左上角x坐标
|
|
|
rect2_y = gap # 第二个矩形框的左上角y坐标
|
|
|
rect2_width = window_width * 2 / 3 - gap + 30 # 第二个矩形框的宽度
|
|
|
rect2_height = window_height * 3 / 4 - gap * 2 # 第二个矩形框的高度
|
|
|
rect3_x = gap # 第三个矩形框的左上角x坐标
|
|
|
rect3_y = window_height * 3 / 4 + gap # 第三个矩形框的左上角y坐标
|
|
|
rect3_width = window_width - gap * 2 # 第三个矩形框的宽度
|
|
|
rect3_height = window_height * 1 / 4 - gap * 2 # 第三个矩形框的高度
|
|
|
global Viewcanvas
|
|
|
# 创建一个画布,用于绘制矩形框,设置画布的大小和背景色
|
|
|
Viewcanvas = tk.Canvas(Root, width=window_width, height=window_height, bg="white")
|
|
|
Viewcanvas.pack() # 将画布添加到主窗口中
|
|
|
# 绘制矩形框,使用不同的颜色和线宽,指定矩形框的左上角和右下角坐标,填充色,边框色和边框宽度
|
|
|
Viewcanvas.create_rectangle(rect1_x, rect1_y, rect1_x + rect1_width, rect1_y + rect1_height,
|
|
|
fill=None, outline="lightblue", width=2)
|
|
|
Viewcanvas.create_rectangle(rect2_x, rect2_y + 20, rect2_x + rect2_width - 20, rect2_y + rect2_height,
|
|
|
fill=None, outline="lightblue", width=2)
|
|
|
Viewcanvas.create_rectangle(rect3_x, rect3_y, rect3_x + rect3_width, rect3_y + rect3_height,
|
|
|
fill=None, outline="lightblue", width=2)
|
|
|
global Text
|
|
|
Text = st.ScrolledText(Root) # 创建一个带滚动条的文本框,用于展示计算过程
|
|
|
Text.place(x=rect3_x, y=rect3_y, width=760, height=rect3_height - 1)
|
|
|
# if __name__ == '__main__':
|
|
|
# Root = tk.Tk()
|
|
|
# # 设置窗口的大小为1200*750
|
|
|
# window_width = 1200 # 窗口的宽度
|
|
|
# window_height = 750 # 窗口的高度
|
|
|
# Root.title("神经网络可视化")
|
|
|
# Root.geometry("1200x750") # 设置窗口的大小和位置
|
|
|
# picture_frame(window_width, window_height)
|
|
|
# Root.mainloop()
|
|
|
|
|
|
def element(path):
|
|
|
# 加载图元对应的图片文件
|
|
|
img=Image.open(path)
|
|
|
# 使用resize方法调整图片
|
|
|
img = img.resize((40, 30))
|
|
|
# 把Image对象转换成PhotoImage对象
|
|
|
img = ImageTk.PhotoImage(img)
|
|
|
# 保存图片的引用,防止被垃圾回收
|
|
|
Root.img = img
|
|
|
return img
|
|
|
|
|
|
def push_button():
|
|
|
button = {}
|
|
|
style = ttk.Style(Root)
|
|
|
# 设置blue.TButton样式
|
|
|
style.configure("blue.TButton", background="LightSlateGray", foreground="black", font=("黑体", 8))
|
|
|
style.map("blue.TButton", background=[('active', 'PowderBlue'), ('pressed', 'red')],
|
|
|
foreground=[('active', 'black'), ('pressed', 'white')])
|
|
|
|
|
|
# 设置green.TButton样式
|
|
|
style.configure("green.TButton", background="MediumSeaGreen", foreground="black", font=("黑体", 8))
|
|
|
style.map("green.TButton", background=[('active', 'PaleGreen'), ('pressed', 'red')],
|
|
|
foreground=[('active', 'black'), ('pressed', 'white')])
|
|
|
|
|
|
# 创建三个按钮,用于触发训练参数的设置、训练、输出模型程序的操作
|
|
|
button1 = ttk.Button(Root, text=" 训练参数设置 ", style="blue.TButton")
|
|
|
button1.place(x=20, y=330)
|
|
|
button['button1'] = button1
|
|
|
button2 = ttk.Button(Root, text=" 训练 ", style="blue.TButton")
|
|
|
button2.place(x=140, y=330)
|
|
|
button['button2'] = button2
|
|
|
button3 = ttk.Button(Root, text=" 输出模型程序 ", style="blue.TButton")
|
|
|
button3.place(x=240, y=330)
|
|
|
button['button3'] = button3
|
|
|
# 创建五个按钮,用于触发操作
|
|
|
button4 = ttk.Button(Root, text=" 加载已有模型 ", style="green.TButton")
|
|
|
button4.place(x=255, y=10)
|
|
|
button['button4'] = button4
|
|
|
button5 = ttk.Button(Root, text=" 新建模型 ", style="green.TButton")
|
|
|
button5.place(x=355, y=10)
|
|
|
button['button5'] = button5
|
|
|
button6 = ttk.Button(Root, text=" 图元参数设置 ", style="green.TButton")
|
|
|
button6.place(x=435, y=10)
|
|
|
button['button6'] = button6
|
|
|
button7 = ttk.Button(Root, text=" 保存模型 ", style="green.TButton")
|
|
|
button7.place(x=535, y=10)
|
|
|
button['button7'] = button7
|
|
|
button8 = ttk.Button(Root, text=" 模型正确性检查 ", style="green.TButton")
|
|
|
button8.place(x=615, y=10)
|
|
|
button['button8'] = button8
|
|
|
button9 = ttk.Button(Root, text="退出", style="green.TButton")
|
|
|
button9.place(x=725, y=10)
|
|
|
button['button9'] = button9
|
|
|
return button
|
|
|
|
|
|
'''
|
|
|
【编程16.19】编制程序:训练函数。
|
|
|
【目的及编程说明】读者通过编程16.19可掌握卷积神经网络的训练过程。其基本流程为输入数据通过前向传播预测出标签值,预测标签与真实标签进行比较得出误差,
|
|
|
反向传播误差值更新卷积层、全连接层参数,直到误差值达到阈值或迭代次数完毕。
|
|
|
基本实践内容:
|
|
|
(1)使用 AllModelObj 中的 SetParaFunc 函数初始化所有需要学习的网络参数。
|
|
|
(2)对于每个 epoch,使用AllModelConn 中的连接对象确定图元或是操作函数之间的关系,使用 AllModelObj 中的 ProcFunc
|
|
|
函数进行前向传播,并使用 AllModelObj 中的 ProcFunc 函数计算误差。再使用 AllModelObj 中的 ProcFunc 函数进行反向传播,
|
|
|
并使用 AllModelObj 中的 SetParaFunc函数更新网络参数。
|
|
|
(3)直到迭代次数停止,将生成的网络结构保存到一个文件中,以便以后使用或加载到其他神经网络中。
|
|
|
(4)若更新训练过程参数,请用户点击训练参数设置,进行更改参数,设置完毕,用户点击保存。
|
|
|
(5)获取训练参数展示区的字符串,解析字符串,对训练函数进行设置。
|
|
|
(6)点击训练按钮,进行训练。
|
|
|
'''
|
|
|
# X3所有基本图元函数
|
|
|
# 定义加载数据集的参数SetLoadData()
|
|
|
|
|
|
# 定义设置卷积参数的函数SetConvPara()
|
|
|
def setconv_para(numbers):
|
|
|
# 从Conv的ParaString得到一个包含所有卷积参数的列表
|
|
|
conv_list = Listinstance[1][5].split()
|
|
|
height, stride = conv_list[numbers - 1].split(',')
|
|
|
height = int(height)# 设置卷积核大小
|
|
|
stride = int(stride)# 设置步长
|
|
|
# 卷积核为3x3的单位矩阵(高斯分布),通常再乘上一个小的数,比如0.01
|
|
|
kernel = np.random.randn(height, height)*0.1
|
|
|
padding = 0# 设置填充,这里假设是0
|
|
|
bias = 0.5# 设置偏置,这里假设是0.5
|
|
|
# 返回ConvPara参数,这里用一个字典来存储
|
|
|
ConvPara = {numbers: {"kernel": kernel,
|
|
|
"kernel_h": height,
|
|
|
"kernel_w": height,
|
|
|
"stride": stride,
|
|
|
"padding": padding,
|
|
|
"bias": bias}}
|
|
|
Conv0 = ModelObj("Conv", 2, "卷积", "ConvProc", "SetConvPara", ".", 340, 60).output()
|
|
|
# 若为第一个卷积层:
|
|
|
if numbers == 1:
|
|
|
# 创建全局变量的卷积图元对象实例
|
|
|
global Conv1
|
|
|
Conv1 = Conv0
|
|
|
# 令Conv1[5]等于{1: {'kernel': array(), 'kernel_h': 5, 'kernel_w': 5, 'stride': 1, 'padding': 0}
|
|
|
Conv1[5] = ConvPara
|
|
|
# 将Conv1[5][1]重新赋值到ConvPara
|
|
|
ConvPara = Conv1[5][1]
|
|
|
# 若为第二个卷积层:
|
|
|
elif numbers == 2:
|
|
|
global Conv2
|
|
|
Conv2 = Conv0
|
|
|
Conv2[5] = ConvPara
|
|
|
ConvPara = Conv2[5][2]
|
|
|
# 若为第三个卷积层:
|
|
|
elif numbers == 3:
|
|
|
global Conv3
|
|
|
Conv3 = Conv0
|
|
|
Conv3[5] = ConvPara
|
|
|
ConvPara = Conv3[5][3]
|
|
|
# 返回ConvPara,即{'kernel': array(), 'kernel_h': 5, 'kernel_w': 5, 'stride': 1, 'padding': 0}
|
|
|
return ConvPara
|
|
|
|
|
|
# 定义卷积函数ConvProc()
|
|
|
def conv_proc(image, ConvPara):
|
|
|
# 获取输入数据的大小,这里假设是单通道的图片
|
|
|
c, image_h, image_w = image.shape
|
|
|
# 获取卷积核的大小和卷积核
|
|
|
kernel_h = ConvPara["kernel_h"]
|
|
|
kernel_w = ConvPara["kernel_w"]
|
|
|
kernel = ConvPara["kernel"]
|
|
|
# 计算输出数据的大小
|
|
|
out_h = (image_h - kernel_h) // ConvPara["stride"] + 1
|
|
|
out_w = (image_w - kernel_w) // ConvPara["stride"] + 1
|
|
|
# 初始化输出数据为零矩阵
|
|
|
output = np.zeros((c, out_h, out_w))
|
|
|
# 对于每一个输出位置,进行卷积操作
|
|
|
# 遍历每个通道
|
|
|
for k in range(c):
|
|
|
# 遍历每个输出位置
|
|
|
for i in range(out_h):
|
|
|
for j in range(out_w):
|
|
|
# 获得步长和偏置
|
|
|
stride = ConvPara["stride"]
|
|
|
bias = ConvPara["bias"]
|
|
|
# 计算卷积操作
|
|
|
output[k, i, j] = np.sum(
|
|
|
image[k, i * stride:i * stride + kernel_h, j * stride:j * stride + kernel_w] * kernel) + bias
|
|
|
# 返回卷积计算后的数组(特征向量)
|
|
|
return output
|
|
|
|
|
|
# 定义设置池化参数的函数
|
|
|
def setpool_para(numbers):
|
|
|
# 从Pool的ParaString得到一个包含所有池化参数的列表
|
|
|
pool_list = Listinstance[2][5].split()
|
|
|
# 池大小和池类型
|
|
|
pool_mode, pool_size = pool_list[numbers-1].split(',')
|
|
|
print(pool_size)
|
|
|
pool_size = int(pool_size)
|
|
|
stride = 2 # 设置步长,这里假设是2
|
|
|
PoolPara = {numbers:{"pool_mode": pool_mode, # 返回PoolPara参数,这里用一个字典来存储
|
|
|
"pool_size": pool_size,
|
|
|
"stride": stride}}
|
|
|
Pool0 = ModelObj("Pool", 3, "最大池化", "MaxPoolProc", "SetPollPara", ".", 150, 150).output()
|
|
|
# 若为第一个池化层:
|
|
|
if numbers == 1:
|
|
|
# 创建全局变量的卷积图元对象实例
|
|
|
global Pool1
|
|
|
Pool1 = Pool0
|
|
|
Pool1[5] = PoolPara
|
|
|
PoolPara = Pool1[5][1]
|
|
|
# 若为第二个卷积层:
|
|
|
elif numbers == 2:
|
|
|
global Pool2
|
|
|
Pool2 = Pool0
|
|
|
Pool2[5] = PoolPara
|
|
|
PoolPara = Pool2[5][2]
|
|
|
# 若为第三个卷积层:
|
|
|
elif numbers == 3:
|
|
|
global Pool3
|
|
|
Pool3 = Pool0
|
|
|
Pool3[5] = PoolPara
|
|
|
PoolPara = Pool3[5][3]
|
|
|
|
|
|
# 返回PoolPara参数
|
|
|
return PoolPara
|
|
|
|
|
|
# 定义池化函数
|
|
|
def pool_proc(image, PoolPara):
|
|
|
# 输入:输入特征图image,PoolPara参数(字典)
|
|
|
# 输出:池化后特征图(数组)
|
|
|
# 从PoolPara参数中获取池化类型,池化窗口大小和步长
|
|
|
pool_mode = PoolPara["pool_mode"]
|
|
|
pool_size = PoolPara["pool_size"]
|
|
|
stride = PoolPara["stride"]
|
|
|
# 计算输出特征图out大小:
|
|
|
c, h, w = image.shape # 获取输入特征图的高度和宽度
|
|
|
out_h = int((h - pool_size) / stride) + 1 # 计算输出特征图的高度
|
|
|
out_w = int((w - pool_size) / stride) + 1 # 计算输出特征图的宽度
|
|
|
out = np.zeros((c, out_h, out_w)) # 初始化输出特征图为全零数组
|
|
|
# 对于输出的每一个位置上计算:
|
|
|
for k in range(c):
|
|
|
for i in range(out_h):
|
|
|
for j in range(out_w):
|
|
|
# 计算输出特征图中每个位置对应的输入特征图中的窗口区域
|
|
|
window = image[k, i * stride:i * stride + pool_size, j * stride:j * stride + pool_size]
|
|
|
# 根据池化类型,取出中间最大值或平均值或最小值,
|
|
|
if pool_mode == "max": # 最大池化
|
|
|
out[k][i][j] = np.max(window)
|
|
|
elif pool_mode == "avg": # 平均池化
|
|
|
out[k][i][j] = np.mean(window)
|
|
|
elif pool_mode == "min": # 最小池化
|
|
|
out[k][i][j] = np.min(window)
|
|
|
else: # 无效的池化类型
|
|
|
raise ValueError("Invalid pooling mode")
|
|
|
# 返回特征图。
|
|
|
return out
|
|
|
|
|
|
# 定义一个函数来设置全连接层的相关参数,这里可以根据实际情况修改或随机生成
|
|
|
def setfullconn_para(image, numbers):
|
|
|
# 获取池化后的图片数组的长度和宽度
|
|
|
data = image
|
|
|
dim = len(data.shape)
|
|
|
if dim == 2 :
|
|
|
height, width = data.shape
|
|
|
elif dim == 3:
|
|
|
c, height, width = data.shape
|
|
|
# 从FullConn的ParaString得到一个包含所有全连接参数的列表
|
|
|
fullconn_list = Listinstance[3][5].split()
|
|
|
# 设置权重矩阵及其输出维度和偏置向量
|
|
|
num_outputs = fullconn_list[numbers - 1]
|
|
|
num_outputs = int(num_outputs)
|
|
|
weights = np.random.randn(num_outputs, height * width)
|
|
|
bias = np.random.randn(1, num_outputs)
|
|
|
# 返回FullConnPara参数,这里用一个字典来存储
|
|
|
FullConnPara = {numbers: {"weights": weights,
|
|
|
"bias": bias,
|
|
|
"num_outputs": num_outputs}}
|
|
|
FullConn0 = ModelObj("FullConn", 4, "全连接", "FullConnProc", "SetFullConnPara", ".", 340, 150).output()
|
|
|
# 第一个卷积层:
|
|
|
if numbers == 1:
|
|
|
# 创建全局变量的全连接图元实例
|
|
|
global Fullconn1
|
|
|
Fullconn1 = FullConn0
|
|
|
# 令Fullconn1[5]等于{1: {'weight': array(), 'bias': array(), 'num_outputs': 7}
|
|
|
Fullconn1[5] = FullConnPara
|
|
|
# 将Fullconn1[5][1]即{'weight': array(), 'bias': array(), 'num_outputs': 7}重新赋值到FullConnPara
|
|
|
FullConnPara = Fullconn1[5][1]
|
|
|
# 第二个卷积层:
|
|
|
elif numbers == 2:
|
|
|
global Fullconn2
|
|
|
Fullconn2 = FullConn0
|
|
|
Fullconn2[5] = FullConnPara
|
|
|
FullConnPara = Fullconn2[5][2]
|
|
|
# 第三个卷积层:
|
|
|
elif numbers == 3:
|
|
|
global Fullconn3
|
|
|
Fullconn3 = FullConn0
|
|
|
Fullconn3[5] = FullConnPara
|
|
|
FullConnPara = Fullconn3[5][3]
|
|
|
# 返回FullConnPara参数
|
|
|
return FullConnPara
|
|
|
|
|
|
# 定义一个函数来完成全连接操作
|
|
|
def fullconn_proc(inputdata, FullConnPara):
|
|
|
# 从FullConnPara参数中获取权重矩阵和偏置向量
|
|
|
weights = FullConnPara["weights"]
|
|
|
bias = FullConnPara["bias"]
|
|
|
# 对输入进行展平处理,变换为单通道的一维数组格式
|
|
|
dim = len(inputdata.shape)
|
|
|
if dim == 2:
|
|
|
inputdata = inputdata.reshape(1, inputdata.shape[0] * inputdata.shape[1])
|
|
|
elif dim == 3:
|
|
|
inputdata = inputdata.reshape(1, inputdata.shape[1] * inputdata.shape[2])
|
|
|
# 计算全连接层的线性变换:inputdata与权重矩阵w进行乘法,再加上偏置向量b
|
|
|
output = np.dot(inputdata, weights.T) + bias
|
|
|
# 返回全连接计算后的数组
|
|
|
return output
|
|
|
|
|
|
# 定义设置非线性参数的函数
|
|
|
def setnonl_para():
|
|
|
# 可以选择"Sigmoid", "ReLU" 或 "Tanh"
|
|
|
# 从Nonline的ParaString得到一个包含所有非线性参数的列表
|
|
|
nonlinearmode = Listinstance[4][5]
|
|
|
# 返回NonLPara参数,这里用一个字典来存储
|
|
|
NonLPara = {"nonlinearmode": nonlinearmode}
|
|
|
# 返回NonLPara参数
|
|
|
return NonLPara
|
|
|
|
|
|
# 定义非线性函数
|
|
|
def nonlinear_proc(inputdata, NonLPara):
|
|
|
# 从NonLPara参数中获取非线性函数类型
|
|
|
nonlinearmode = NonLPara["nonlinearmode"]
|
|
|
# 判断nonlinearmode,进行相应的计算
|
|
|
if nonlinearmode == "Sigmoid":
|
|
|
# Sigmoid函数,将任何实数的输入映射到0和1之间的输出
|
|
|
output = 1 / (1 + np.exp(-inputdata))
|
|
|
# 求出Sigmoid函数的导数,用于全连接层权重矩阵和偏执向量的调整
|
|
|
output_derivative = (1 / (1 + np.exp(-inputdata))) * (1 - (1 / (1 + np.exp(-inputdata))))
|
|
|
elif nonlinearmode == "ReLU":
|
|
|
# ReLU函数,将负数输入置为0,而正数输入保持不变
|
|
|
output = np.maximum(inputdata, 0)
|
|
|
# 求出ReLU函数的导数,用于全连接层权重矩阵和偏执向量的调整
|
|
|
output_derivative = np.where(inputdata > 0, 1, 0)
|
|
|
elif nonlinearmode == "Tanh":
|
|
|
# Tanh函数,将任何实数的输入映射到-1和1之间的输出
|
|
|
output = np.tanh(inputdata)
|
|
|
# 求出Tanh函数的导数,用于全连接层权重矩阵和偏执向量的调整
|
|
|
output_derivative = 1 - np.tanh(inputdata) ** 2
|
|
|
else:
|
|
|
# 非法的非线性类型,抛出异常
|
|
|
raise ValueError("Invalid nonlinear mode")
|
|
|
# 返回计算后的值
|
|
|
return output,output_derivative
|
|
|
|
|
|
# 定义设置分类函数参数的函数
|
|
|
def setclassify_para():
|
|
|
# 设定阈值,可以根据你的数据和任务来调整阈值
|
|
|
threshold = float(Listinstance[5][5])
|
|
|
# 返回ClassifyPara参数,这里用一个字典来存储
|
|
|
ClassifyPara = {"threshold": threshold}
|
|
|
# 返回ClassifyPara参数
|
|
|
return ClassifyPara
|
|
|
|
|
|
# 定义分类函数
|
|
|
def classifier_proc(inputdata, ClassifyPara):
|
|
|
# 找到概率超过阈值的标签分类,就是分类结果
|
|
|
# 假设输入数据是一个一维数组,表示每个类别的概率
|
|
|
# 假设输出数据是一个整数,表示预测的类别编号
|
|
|
# 如果没有任何类别的概率超过阈值,就返回-1表示无法分类
|
|
|
# 定义softmax函数
|
|
|
def softmax(x):
|
|
|
# 减去最大值,防止数值溢出
|
|
|
x -= np.max(x)
|
|
|
# 计算指数和归一化
|
|
|
return np.exp(x) / np.sum(np.exp(x))
|
|
|
# 从ClassifyPara参数中获取阈值
|
|
|
threshold = ClassifyPara["threshold"]
|
|
|
# 调用softmax函数,得到概率分布向量
|
|
|
prob = softmax(inputdata)
|
|
|
# 如果概率高于阈值,就将该类别加入输出结果
|
|
|
prob1 = prob[prob >= threshold]
|
|
|
# 使用where()函数来返回等于概率最大值的元素的索引
|
|
|
index = np.where(prob == max(prob1))
|
|
|
# 使用item()方法来将索引转换为标准Python标量
|
|
|
output = index[1].item(0) + 1
|
|
|
# 返回分类标签
|
|
|
return output
|
|
|
|
|
|
# 定义设置误差参数的函数
|
|
|
def seterror_para():
|
|
|
# 确定参数信息: 标签类别,损失函数类型
|
|
|
# 从FullConn的ParaString得到一个包含所有全连接参数的列表
|
|
|
fullconn_list = Listinstance[3][5].split()
|
|
|
# 标签类别为最后一个全连接层的输出维度
|
|
|
num_outputs = fullconn_list[-1]
|
|
|
num_outputs = int(num_outputs)
|
|
|
label_list = list(range(1, num_outputs + 1))
|
|
|
# 假设损失函数类型为交叉熵(Cross Entropy Error,CEE)
|
|
|
# 从Error的ParaString得到一个包含所有全连接参数的列表
|
|
|
error_list = Listinstance[6][5].split()
|
|
|
# 设置权重矩阵及其输出维度和偏置向量
|
|
|
loss_type = error_list[-1]
|
|
|
# 返回ErrorProc参数,这里用一个元组来存储
|
|
|
ErrorPara = (label_list, loss_type)
|
|
|
# 返回ErrorPara参数
|
|
|
return ErrorPara
|
|
|
|
|
|
# 定义计算误差的函数
|
|
|
def error_proc(input, right_label, ErrorPara):
|
|
|
# 将真实标签类别label转换为one-hot编码形式
|
|
|
# 读取标签列表和损失函数类型
|
|
|
label_list, loss_type = ErrorPara
|
|
|
# 创建一个单位矩阵,大小为标签类别的个数
|
|
|
one_hot_matrix = np.eye(len(label_list))
|
|
|
# 找到标签在标签列表中的索引位置
|
|
|
index = [label_list.index(l) for l in right_label]
|
|
|
index = [x-1 for x in right_label]
|
|
|
# 从one-hot矩阵中取出对应的向量
|
|
|
label_one_hot = np.take(one_hot_matrix, index, axis=0)
|
|
|
# 确定损失函数类别,实现不同的损失函数,计算输入值与label之间的误差
|
|
|
if loss_type == "CEE":
|
|
|
# 使用交叉熵损失函数,公式为:-sum(label_one_hot * log(input)) / n
|
|
|
loss = -np.sum(label_one_hot * np.log(input)) / len(input)
|
|
|
elif loss_type == "MSE":
|
|
|
# 使用均方误差损失函数,公式为:sum((input - label_one_hot) ** 2) / n
|
|
|
loss = np.sum((input - label_one_hot) ** 2) / len(input)
|
|
|
elif loss_type == "MAE":
|
|
|
# 使用平均绝对误差损失函数,公式为:sum(abs(input - label_one_hot)) / n
|
|
|
loss = np.sum(np.abs(input - label_one_hot)) / len(input)
|
|
|
else:
|
|
|
# 如果损失函数类型不在以上三种中,抛出异常
|
|
|
raise ValueError("Invalid loss type")
|
|
|
# 返回误差值loss
|
|
|
return loss
|
|
|
|
|
|
# 定义一个卷积核调整函数,输入为卷积层的输入数据数组、卷积核、偏置、误差值,输出为更新后的卷积核、偏置
|
|
|
def update_conv_layer(input, kernel, bias, loss):
|
|
|
kernel_grad_list = []
|
|
|
bias_grad = 0 # 初始化偏置项梯度为零
|
|
|
for c in input:
|
|
|
# 计算卷积核和偏置项的梯度
|
|
|
kernel_grad = np.zeros_like(kernel) # 初始化卷积核梯度为零矩阵
|
|
|
for i in range(loss.shape[0]): # 遍历误差值矩阵的行
|
|
|
for j in range(loss.shape[1]): # 遍历误差值矩阵的列
|
|
|
# 将输入数据数组中对应的子矩阵旋转180度,与误差值相乘,累加到卷积核梯度上
|
|
|
kernel_grad += np.rot90(c[i:i + kernel_grad.shape[0], j:j + kernel_grad.shape[0]], 2) * loss[i, j]
|
|
|
# 将误差值累加到偏置项梯度上
|
|
|
bias_grad += loss[i, j]
|
|
|
kernel_grad_list.append(kernel_grad)
|
|
|
# 使用stack函数沿着第0个轴把一百个a数组堆叠起来
|
|
|
result = np.stack(kernel_grad_list, axis=0)
|
|
|
kernel_grad = np.sum(result, axis=0)/len(input) # 沿着第0个维度求和
|
|
|
# 设置学习率为0.01
|
|
|
learning_rate = 0.01
|
|
|
# 更新卷积核和偏置项参数
|
|
|
kernel = kernel - learning_rate * kernel_grad # 卷积核参数减去学习率乘以卷积核梯度
|
|
|
bias = bias - learning_rate * bias_grad # 偏置项参数减去学习率乘以偏置项梯度
|
|
|
# 返回更新后的卷积核、偏置
|
|
|
return kernel, bias
|
|
|
|
|
|
# 定义一个全连接层的权重矩阵和偏置向量的调整函数,可以选择不同的激活函数
|
|
|
def update_parameters(input_data, weight_matrix, bias_vector, loss, output_derivative):
|
|
|
# 根据激活函数的参数选择相应的函数和导数
|
|
|
# 计算权重矩阵和偏置向量的梯度,使用链式法则
|
|
|
weight_matrix_grad = np.dot(input_data.T, loss * output_derivative)
|
|
|
bias_vector_grad = np.sum(loss * output_derivative, axis=0)
|
|
|
learning_rate = 0.01
|
|
|
# 更新权重矩阵和偏置向量
|
|
|
weight_matrix = weight_matrix - learning_rate * weight_matrix_grad
|
|
|
bias_vector = bias_vector - learning_rate * bias_vector_grad
|
|
|
# 返回更新后的权重矩阵和偏置向量
|
|
|
return weight_matrix, bias_vector
|
|
|
|
|
|
# X4交互式构建卷积神经网络模型
|
|
|
# 定义图元拖拽开始的函数
|
|
|
def on_drag_start(event):
|
|
|
global Dragged_item # 记录被拖拽的图元id
|
|
|
global Original_x # 记录图元原来的x坐标
|
|
|
global Original_y # 记录图元原来的y坐标
|
|
|
global Offset_x # 记录鼠标相对于图元的x偏移量
|
|
|
global Offset_y # 记录鼠标相对于图元的y偏移量
|
|
|
# 获取当前图元的id
|
|
|
# 这个物体的id是由tkinter自动分配的。当在画布上创建一个物体时,它会返回一个唯一的整数id,可以用它来引用这个物体
|
|
|
Dragged_item = event.widget.find_closest(event.x, event.y)
|
|
|
|
|
|
Original_x = event.widget.coords(Dragged_item)[0] # 获取当前图元的x坐标
|
|
|
Original_y = event.widget.coords(Dragged_item)[1] # 获取当前图元的y坐标
|
|
|
Offset_x = Original_x - event.x # 计算鼠标相对于图元的x偏移量
|
|
|
Offset_y = Original_y - event.y # 计算鼠标相对于图元的y偏移量
|
|
|
|
|
|
# 定义图元拖拽移动的函数
|
|
|
def on_drag_motion(event):
|
|
|
x = event.x + Offset_x # 计算图元新的x坐标
|
|
|
y = event.y + Offset_y # 计算图元新的y坐标
|
|
|
event.widget.coords(Dragged_item, x, y) # 更新图元的位置
|
|
|
|
|
|
def entity_loading_dataset(event):
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
y = event.y + Offset_y
|
|
|
|
|
|
Items3.append(0) # 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
new_item1 = Viewcanvas.create_image(x, y, image=List_image[0]) # 复制一个图片,表示数据集1
|
|
|
Viewcanvas.create_text(x, y + 45, text='数据集')# 复制图元对象的标签
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)# 恢复图元的原位置
|
|
|
Items1.append((new_item1, x, y))# 将新图元的id和坐标添加到列表中
|
|
|
global Yt
|
|
|
Items_y = Items1[0] # 将新图元的y坐标取出,方便后续图元水平排列
|
|
|
Yt = Items_y[2]
|
|
|
# 创建一个tkinter根窗口,并隐藏它
|
|
|
root1 = tk.Tk()
|
|
|
root1.withdraw()
|
|
|
|
|
|
DataPara = setload_data() # 调用X3中setload_data()函数,获取加载数据集的参数
|
|
|
global Train_images
|
|
|
global Test_images
|
|
|
Train_images, Test_images = load_data(DataPara) # 调用X3中load_data()函数,根据参数加载数据集
|
|
|
# 打印一些信息,检查是否正确加载
|
|
|
print("训练集图片的形状:", Train_images.shape)
|
|
|
print("测试集图片的形状:", Test_images.shape)
|
|
|
|
|
|
# 创建弹出窗口
|
|
|
def creat_window(title):
|
|
|
top = tk.Toplevel(Root)
|
|
|
top.geometry("300x350")
|
|
|
top.title(title)
|
|
|
return top
|
|
|
# 输入框
|
|
|
def create_input_box(top, text,value):
|
|
|
box_label = tk.Label(top, text=text)
|
|
|
box_label.pack(padx=10, pady=10)
|
|
|
box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3
|
|
|
box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象
|
|
|
box_size_entry.pack(padx=20, pady=20)
|
|
|
return box_size_entry
|
|
|
# 下拉框
|
|
|
def create_dropdown_box(top, text, listvalues):
|
|
|
# 创建一个下拉框,用于选择
|
|
|
box_mode_label = tk.Label(top, text=text)
|
|
|
box_mode_label.pack(padx=10, pady=10)
|
|
|
box_mode_combobox = ttk.Combobox(top)
|
|
|
box_mode_combobox["values"] = listvalues
|
|
|
box_mode_combobox.pack(padx=20, pady=15)
|
|
|
return box_mode_combobox
|
|
|
|
|
|
def convolution(event):
|
|
|
global Items3
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
Items3.append(1) # 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[1]) # 复制一个图片,表示卷积层
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y) # 恢复图元的原位置
|
|
|
Items1.append((new_item1, x, Yt)) # 将新图元的id和坐标添加到列表中
|
|
|
top = creat_window('卷积参数配置') # 创建弹出窗口
|
|
|
kernel_size_entry = create_input_box(top,"卷积核大小:",3) # 创建一个输入框,获取卷积核大小
|
|
|
stride_entry = create_input_box(top,"卷积步长:",1) # 创建一个输入框,获取卷积步长
|
|
|
def get_input(): # 创建
|
|
|
# 获取输入框的内容
|
|
|
global Listinstance
|
|
|
result1 = int(kernel_size_entry.get())
|
|
|
result2 = int(stride_entry.get())
|
|
|
Viewcanvas.create_text(x, Yt + 40, text=str(result1) + "*" + str(result1) + "卷积") # 创建图元对象的标签
|
|
|
if Listinstance[1][5] == ".":
|
|
|
Listinstance[1][5] = str(result1) + "," + str(result2)
|
|
|
else:
|
|
|
Listinstance[1][5] = Listinstance[1][5] + " " + str(result1) + "," + str(result2)
|
|
|
top.destroy()# 关闭窗口
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def pooling(event):
|
|
|
global Items3
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(2)
|
|
|
# 复制一个图片,表示池化层
|
|
|
new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[2])
|
|
|
# 恢复图元的原位置
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, Yt))
|
|
|
# 创建弹出窗口
|
|
|
top=creat_window("池化参数配置")
|
|
|
pool_type_combobox=create_dropdown_box(top, "池化池类型:", ["max", "avg", "min"])# 创建一个下拉框,用于选择池化类型
|
|
|
pool_size_entry = create_input_box(top, "池化池大小:", 2)# 创建一个输入框,用于输入池大小
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
# 从下拉框中获取池化类型
|
|
|
pool_type = pool_type_combobox.get()
|
|
|
# 从输入框中获取池大小
|
|
|
pool_size = int(pool_size_entry.get())
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, Yt + 40, text=str(pool_size) + "*" + str(pool_size) + str(pool_type) + "池化")
|
|
|
if Listinstance[2][5] == ".":
|
|
|
Listinstance[2][5] = str(pool_type) + "," + str(pool_size)
|
|
|
else:
|
|
|
Listinstance[2][5] = Listinstance[2][5] + " " + str(pool_type) + "," + str(pool_size)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def fully_connected(event):
|
|
|
global Items3
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(3)
|
|
|
# 复制一个图片,表示全连接层
|
|
|
new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[3])
|
|
|
# 恢复图元的原位置
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, Yt))
|
|
|
top = tk.Toplevel(Root) # 创建弹出窗口
|
|
|
top.geometry("300x250")
|
|
|
top.title("全连接参数配置")
|
|
|
num_outputs_entry = create_input_box(top, "全连接输出维度:", 10)
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
global Listinstance
|
|
|
# 从下拉框中获取输出维度
|
|
|
num = int(num_outputs_entry.get())
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, Yt + 40, text=str(num) + "--全连接")
|
|
|
if Listinstance[3][5] == ".":
|
|
|
Listinstance[3][5] = str(num)
|
|
|
else:
|
|
|
Listinstance[3][5] = Listinstance[3][5] + " " + str(num)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack(padx=20, pady=50)
|
|
|
|
|
|
def nonlinear(event):
|
|
|
global Items3
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(4)
|
|
|
# 复制一个图片,表示非线性函数图元
|
|
|
new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[4])
|
|
|
# 恢复图元的原位置
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, Yt))
|
|
|
top = creat_window('非线性参数配置') # 创建弹出窗口
|
|
|
# 创建一个下拉框,用于选择非线性类型
|
|
|
nonlinear_mode_combobox = create_dropdown_box(top, "非线性类型", ["Sigmoid", "ReLU", "Tanh"])# 创建一个下拉框,用于选择池化类型
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
global Listinstance
|
|
|
# 从下拉框中获取池化类型
|
|
|
nonlinear_mode = nonlinear_mode_combobox.get()
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, Yt + 40, text=str(nonlinear_mode) + "处理")
|
|
|
if Listinstance[4][5] == ".":
|
|
|
Listinstance[4][5] = str(nonlinear_mode)
|
|
|
else:
|
|
|
Listinstance[4][5] = Listinstance[4][5] + " " + str(nonlinear_mode)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def class_ification(event):
|
|
|
global Listinstance
|
|
|
global Viewcanvas
|
|
|
global Items3
|
|
|
x = event.x + Offset_x
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(5)
|
|
|
# 复制一个图片,表示分类器
|
|
|
new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[5])
|
|
|
# 恢复图元的原位置
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, Yt))
|
|
|
top = tk.Toplevel(Root) # 创建弹出窗口
|
|
|
top.geometry("300x250")
|
|
|
top.title("分类参数配置")
|
|
|
# 创建一个输入框,用于输入阈值
|
|
|
threshold_label = tk.Label(top, text="阈值:")
|
|
|
threshold_label.pack(padx=10, pady=10)
|
|
|
threshold = tk.DoubleVar(top, value=0.001) # 创建一个DoubleVar对象,并设置默认值为0.001
|
|
|
threshold_entry = tk.Entry(top, textvariable=threshold) # 关联DoubleVar对象
|
|
|
threshold_entry.pack(padx=20, pady=20)
|
|
|
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
# 从输入框中获取阈值
|
|
|
threshold = float(threshold_entry.get())
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, Yt + 40, text="输出分类结果")
|
|
|
if Listinstance[5][5] == ".":
|
|
|
Listinstance[5][5] = str(threshold)
|
|
|
else:
|
|
|
Listinstance[5][5] = Listinstance[5][5] + " " + str(threshold)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def error_calculation(event):
|
|
|
global Items3
|
|
|
global Viewcanvas
|
|
|
x = event.x + Offset_x
|
|
|
y = event.y + Offset_y
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(6)
|
|
|
# 复制一个图片,表示误差计算
|
|
|
new_item1 = Viewcanvas.create_image(x, y, image=List_image[6])
|
|
|
# 恢复图元的原位置
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y)
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, y))
|
|
|
global x_loss
|
|
|
global y_loss
|
|
|
x_loss = x
|
|
|
y_loss = y
|
|
|
# 创建弹出窗口
|
|
|
top = creat_window('误差参数配置')
|
|
|
# 创建一个下拉框,用于选择误差类型
|
|
|
loss_type_combobox=create_dropdown_box(top, '', ["CEE", "MSE", "MAE"])
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
# 从下拉框中获取池化类型
|
|
|
loss_type = loss_type_combobox.get()
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, y_loss + 40,
|
|
|
text=str(loss_type) + "误差计算")
|
|
|
if Listinstance[6][5] == ".":
|
|
|
Listinstance[6][5] = str(loss_type)
|
|
|
else:
|
|
|
Listinstance[6][5] = Listinstance[6][5] + " " + str(loss_type)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def adjust_one(event):
|
|
|
global Viewcanvas
|
|
|
global Items1
|
|
|
global Items3
|
|
|
x = event.x + Offset_x
|
|
|
y = event.y + Offset_y
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(7)
|
|
|
# 复制一个图片,表示调整操作
|
|
|
new_item1 = Viewcanvas.create_image(x, y_loss - 20, image=List_image[7])
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, y_loss + 20, text='调整1')
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y) # 恢复图元的原位置
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, y))
|
|
|
|
|
|
|
|
|
def adjust_two(event):
|
|
|
global Viewcanvas
|
|
|
global Items1
|
|
|
global Items3
|
|
|
x = event.x + Offset_x
|
|
|
y = event.y + Offset_y
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(8)
|
|
|
# 复制一个图片,表示调整操作
|
|
|
new_item1 = Viewcanvas.create_image(x, y_loss + 20, image=List_image[7])
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, y_loss + 60, text='调整2')
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y) # 恢复图元的原位置
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, y))
|
|
|
|
|
|
|
|
|
def adjust_three(event):
|
|
|
global Viewcanvas
|
|
|
global Items1
|
|
|
global Items3
|
|
|
x = event.x + Offset_x
|
|
|
y = event.y + Offset_y
|
|
|
# 记录被拖拽的原始图元的id,方便后续进行模型的正确性检查
|
|
|
Items3.append(9)
|
|
|
# 复制一个图片,表示调整操作
|
|
|
new_item1 = Viewcanvas.create_image(x, y, image=List_image[7])
|
|
|
# 复制对象的标签
|
|
|
Viewcanvas.create_text(x, y + 40, text='调整3')
|
|
|
event.widget.coords(Dragged_item, Original_x, Original_y) # 恢复图元的原位置
|
|
|
# 将新图元的id和坐标添加到列表中
|
|
|
Items1.append((new_item1, x, y))
|
|
|
|
|
|
def connecting_line():
|
|
|
global Viewcanvas
|
|
|
global Items2
|
|
|
last_item = Items1[-1] # 获取最后一个图元的id和坐标
|
|
|
prev_item = Items1[-2] # 获取倒数第二个图元的id和坐标
|
|
|
# 获取最后一个图元的边界框坐标,形式为(x1,y1,x2,y2),其中(x1,y1)是左上角,(x2,y2)是右下角
|
|
|
last_bbox = Viewcanvas.bbox(last_item[0])
|
|
|
# 获取倒数第二个图元的边界框坐标,形式同上
|
|
|
prev_bbox = Viewcanvas.bbox(prev_item[0])
|
|
|
# 计算线条的起点和终点,使其与图元的边界对齐,并留出一定距离以避免重叠
|
|
|
start_x = prev_bbox[2] + 5 # 起点x坐标为倒数第二个图元右边界加10像素
|
|
|
start_y = prev_bbox[3] - (prev_bbox[3] - prev_bbox[1]) / 2 # 起点y坐标为倒数第二个图元垂直中心
|
|
|
end_x = last_bbox[0] # 终点x坐标为最后一个图元左边界
|
|
|
end_y = last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2 # 终点y坐标为最后一个图元垂直中心
|
|
|
# 创建一条线,带有箭头,并设置箭头形状、大小、颜色和平滑度等参数
|
|
|
if Items3[-1] < 6:
|
|
|
new_item = Viewcanvas.create_line(start_x, start_y, end_x, end_y, arrow=tk.LAST,
|
|
|
arrowshape=(16, 20, 4), fill='lightblue', smooth=True)
|
|
|
if Items3[-1] == 1:
|
|
|
x = last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2
|
|
|
y = last_bbox[1]
|
|
|
Conv_xy_list.append((x, y))
|
|
|
if Items3[-1] == 3:
|
|
|
x = last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2
|
|
|
y = last_bbox[1]
|
|
|
Fullconn_xy_list.append((x, y))
|
|
|
|
|
|
elif Items3[-1] == 6:
|
|
|
# 计算折线的中间点,使其与图元的边界对齐,并留出一定距离以避免重叠
|
|
|
mid_x = prev_bbox[2] - (prev_bbox[2] - prev_bbox[0]) / 2 # 中间点x坐标为起点和终点的平均值
|
|
|
mid_y = prev_bbox[1] - 80 # 中间点y坐标为起点y坐标减20像素
|
|
|
# 创建一条折线,带有箭头,并设置箭头形状、大小、颜色和平滑度等参数
|
|
|
x1 = prev_bbox[2] - (prev_bbox[2] - prev_bbox[0]) / 2
|
|
|
y1 = prev_bbox[1]
|
|
|
x2 = last_bbox[2]
|
|
|
y2 = last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2
|
|
|
new_item = Viewcanvas.create_line(x1, y1, mid_x, mid_y, x2, y2, arrow=tk.LAST,
|
|
|
arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
|
|
|
elif Items3[-1] == 7:
|
|
|
x1 = x_loss
|
|
|
y1 = y_loss
|
|
|
x2, y2 = Conv_xy_list[0]
|
|
|
new_item = Viewcanvas.create_line(x1, y1, last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2,
|
|
|
last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2, x2, y2, arrow=tk.LAST,
|
|
|
arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
|
|
|
Conv_xy_list.remove(Conv_xy_list[0])
|
|
|
elif Items3[-1] == 8:
|
|
|
x1 = x_loss
|
|
|
y1 = y_loss
|
|
|
x2, y2 = Fullconn_xy_list[0]
|
|
|
new_item = Viewcanvas.create_line(x1, y1, last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2,
|
|
|
last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2, x2, y2, arrow=tk.LAST,
|
|
|
arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
|
|
|
Fullconn_xy_list.remove(Fullconn_xy_list[0])
|
|
|
Items2.append(new_item)
|
|
|
|
|
|
def on_drag(obj_id1,event):
|
|
|
while obj_id1 == 4: # 如果拖拽的是图元加载数据集
|
|
|
entity_loading_dataset(event)
|
|
|
break
|
|
|
while obj_id1 == 6: # 卷积
|
|
|
convolution(event)
|
|
|
break
|
|
|
while obj_id1 == 8: # 池化
|
|
|
pooling(event)
|
|
|
break
|
|
|
while obj_id1 == 10: # 全连接
|
|
|
fully_connected(event)
|
|
|
break
|
|
|
while obj_id1 == 12: # 非线性
|
|
|
nonlinear(event)
|
|
|
break
|
|
|
while obj_id1 == 14: # 分类
|
|
|
class_ification(event)
|
|
|
break
|
|
|
while obj_id1 == 16: # 误差计算
|
|
|
error_calculation(event)
|
|
|
break
|
|
|
while obj_id1 == 18: # 调整1
|
|
|
adjust_one(event)
|
|
|
break
|
|
|
while obj_id1 == 20: # 调整2
|
|
|
adjust_two(event)
|
|
|
break
|
|
|
while obj_id1 == 22: # 调整3
|
|
|
adjust_three(event)
|
|
|
break
|
|
|
|
|
|
# 定义图元拖拽结束的函数
|
|
|
def on_drag_end(event):
|
|
|
obj_id1 = Dragged_item[0] # 获取被拖拽的图元的id
|
|
|
# 使用 print(obj_id1) 从而查看各个图元的id,此步省略
|
|
|
# 根据图元的类型,复制相应的图片到右侧画布
|
|
|
on_drag(obj_id1,event)
|
|
|
# 如果列表中有至少两个图元,就创建一条线,连接最后两个图元的中心点
|
|
|
while len(Items1) >= 2:
|
|
|
connecting_line()
|
|
|
break
|
|
|
def element_binding(obj_x, obj_y, image, y, text):
|
|
|
global Viewcanvas
|
|
|
# 创建图元对象
|
|
|
l1 = Viewcanvas.create_image(obj_x, obj_y, image=image)
|
|
|
# 创建图元对象的标签
|
|
|
Viewcanvas.create_text(obj_x - 40, obj_y + y, text=text, font=("黑体", 8))
|
|
|
return l1
|
|
|
|
|
|
def create_left_element(AllModelObj,List_image):
|
|
|
global Viewcanvas
|
|
|
# 遍历AllModelObj列表,在窗口左侧创建图元菜单
|
|
|
for obj in AllModelObj:
|
|
|
# 获取图元对象的类型、标签等信息
|
|
|
obj_id = obj[0]
|
|
|
obj_type = obj[1]
|
|
|
obj_label = obj[2]
|
|
|
# 并且要根据需求调整每个对象的位置
|
|
|
obj_x = obj[6] # 根据对象的id计算x坐标
|
|
|
obj_y = obj[7] # 根据对象的id计算y坐标
|
|
|
# 根据对象的类型,绘制相应的图形
|
|
|
if obj_type == 1: # 加载数据集
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[0], 3, " 加载" + "\n" + "数据集")
|
|
|
elif obj_type == 2: # 卷积
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[1], 0, "卷积")
|
|
|
elif obj_type == 3: # 池化
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[2], 0, "池化")
|
|
|
elif obj_type == 4: # 全连接
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[3], 0, "全连接" + "\n" + " 函数")
|
|
|
elif obj_type == 5: # 非线性
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[4], 0, "非线性" + "\n" + " 函数")
|
|
|
elif obj_type == 6: # 分类
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[5], 0, "类别")
|
|
|
elif obj_type == 7: # 误差计算
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[6], 0, "误差")
|
|
|
elif obj_type == 8: # 调整
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整1")
|
|
|
elif obj_type == 9: # 调整
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整2")
|
|
|
elif obj_type == 10: # 调整
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整3")
|
|
|
elif obj_type == 11: # 调整
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[8], 3, "主数据连接线")
|
|
|
elif obj_type == 12: # 调整
|
|
|
l1 = element_binding(obj_x, obj_y, List_image[9], 3, "参数连接线")
|
|
|
# 为左边菜单的每个图元绑定鼠标按下、移动和松开的事件
|
|
|
Viewcanvas.tag_bind(l1, "<Button-1>", on_drag_start)
|
|
|
Viewcanvas.tag_bind(l1, "<B1-Motion>", on_drag_motion)
|
|
|
Viewcanvas.tag_bind(l1, "<ButtonRelease-1>", on_drag_end)
|
|
|
|
|
|
def train_setting():
|
|
|
global Train_list
|
|
|
Train_list = []
|
|
|
top = creat_window("训练参数配置")# 创建弹出的参数配置窗口
|
|
|
epoch_entry = create_input_box(top,"训练轮数:", 4)# 创建一个输入框,获取训练轮数
|
|
|
rate_entry=create_input_box(top, '学习率:', 0.1)# 创建一个输入框,获取学习率
|
|
|
batch_size_entry=create_input_box(top, "批次:", 60)# 创建一个输入框,获取批次
|
|
|
# 创建
|
|
|
def get_input():
|
|
|
# 获取输入框的内容
|
|
|
result1 = int(epoch_entry.get())
|
|
|
result2 = float(rate_entry.get())
|
|
|
result3 = int(batch_size_entry.get())
|
|
|
Train_list.append(result1)
|
|
|
Train_list.append(result2)
|
|
|
Train_list.append(result3)
|
|
|
# 关闭窗口
|
|
|
top.destroy()
|
|
|
button = tk.Button(top, text="获取信息", command=get_input)
|
|
|
button.pack()
|
|
|
|
|
|
def conn_forward(counts,layer,outputs):
|
|
|
# 若为第一层卷积层且该层的图元实例Conv1还未创建
|
|
|
if counts[layer] == 1 and Conv1 is None:
|
|
|
# 调用SetConvPara()函数,获取卷积层1参数
|
|
|
ConvPara = setconv_para(counts[layer])
|
|
|
# 若图元实例Conv1已经被定义,说明需要根据反向传播调整卷积核
|
|
|
if counts[layer] == 1 and Conv1 is not None:
|
|
|
ConvPara = Conv1[5][1]
|
|
|
if counts[layer] == 2 and Conv2 is None:
|
|
|
ConvPara = setconv_para(counts[layer])
|
|
|
if counts[layer] == 2 and Conv2 is not None:
|
|
|
ConvPara = Conv2[5][2]
|
|
|
if counts[layer] == 3 and Conv3 is None:
|
|
|
ConvPara = setconv_para(counts[layer])
|
|
|
if counts[layer] == 3 and Conv3 is not None:
|
|
|
ConvPara = Conv3[5][3]
|
|
|
# 初始化一个空列表来存储池化后的结果
|
|
|
new_outputs = []
|
|
|
# 对每一张图片进行前向传播
|
|
|
for output in outputs:
|
|
|
# 获取矩阵的维度
|
|
|
dim = len(output.shape)
|
|
|
# 如果是二维矩阵,则转化为三维矩阵
|
|
|
if dim == 2:
|
|
|
image_h, image_w = output.shape
|
|
|
output = np.reshape(output, (1, image_h, image_w))
|
|
|
# 调用ConvProc()函数,根据ConvPara参数完成卷积计算
|
|
|
output = conv_proc(output, ConvPara)
|
|
|
new_outputs.append(output)
|
|
|
# 若为三维矩阵,则保持不变直接卷积处理
|
|
|
elif dim == 3:
|
|
|
output = conv_proc(output, ConvPara)
|
|
|
new_outputs.append(output)
|
|
|
# 将new_outputs赋值给outputs
|
|
|
outputs = new_outputs
|
|
|
# 只打印第一张图片信息,检查是否正确计算
|
|
|
print("卷积处理结果:")
|
|
|
print(outputs[0])
|
|
|
print("")
|
|
|
return outputs
|
|
|
def pool_forward(counts,layer,outputs):
|
|
|
global Pool1
|
|
|
global Pool2
|
|
|
global Pool3
|
|
|
if counts[layer] == 1 and Pool1 is None:
|
|
|
# 调用设置池化层参数的函数,获取池化参数
|
|
|
PoolPara = setpool_para(counts[layer])
|
|
|
if counts[layer] == 1 and Pool1 is not None:
|
|
|
PoolPara = Pool1[5][1]
|
|
|
if counts[layer] == 2 and Pool2 is None:
|
|
|
PoolPara = setpool_para(counts[layer])
|
|
|
if counts[layer] == 2 and Pool2 is not None:
|
|
|
PoolPara = Pool2[5][2]
|
|
|
if counts[layer] == 3 and Pool3 is None:
|
|
|
PoolPara = setpool_para(counts[layer])
|
|
|
if counts[layer] == 3 and Pool3 is not None:
|
|
|
PoolPara = Pool3[5][3]
|
|
|
# 初始化一个空列表来存储池化后的结果
|
|
|
new_outputs = []
|
|
|
# 对outputs列表中的每个元素调用PoolProc函数,并将结果添加到new_outputs列表中
|
|
|
for output in outputs:
|
|
|
new_outputs.append(pool_proc(output, PoolPara))
|
|
|
# 将new_outputs赋值给outputs
|
|
|
outputs = new_outputs
|
|
|
if counts[layer] == 1:
|
|
|
Pool1.append(outputs)
|
|
|
if counts[layer] == 2:
|
|
|
Pool2.append(outputs)
|
|
|
if counts[layer] == 3:
|
|
|
Pool3.append(outputs)
|
|
|
print("池化处理结果:")
|
|
|
print(outputs[0])
|
|
|
print("")
|
|
|
return outputs
|
|
|
|
|
|
def fullconn_forward(counts,layer,outputs):
|
|
|
output = outputs[0]
|
|
|
# 若图元实例Fullconn1未被定义
|
|
|
if counts[layer] == 1 and Fullconn1 is None:
|
|
|
# 调用设置全连接层参数的函数,获取全连接参数
|
|
|
FullConnPara = setfullconn_para(output, counts[layer])
|
|
|
# 若图元实例Fullconn1已经被定义,说明需要根据反向传播调整权重矩阵和偏置向量
|
|
|
if counts[layer] == 1 and Fullconn1 is not None:
|
|
|
FullConnPara = Fullconn1[5][1]
|
|
|
if counts[layer] == 2 and Fullconn2 is None:
|
|
|
FullConnPara = setfullconn_para(output, counts[layer])
|
|
|
if counts[layer] == 2 and Fullconn2 is not None:
|
|
|
FullConnPara = Fullconn2[5][2]
|
|
|
if counts[layer] == 3 and Fullconn3 is None:
|
|
|
FullConnPara = setfullconn_para(output, counts[layer])
|
|
|
if counts[layer] == 3 and Fullconn3 is not None:
|
|
|
FullConnPara = Fullconn3[5][3]
|
|
|
# 初始化一个空列表来存储池化后的结果
|
|
|
new_outputs = []
|
|
|
# 对outputs列表中的每个元素调用FullConnProc函数,并将结果添加到new_outputs列表中
|
|
|
for output in outputs:
|
|
|
new_outputs.append(fullconn_proc(output, FullConnPara))
|
|
|
# 将new_outputs赋值给outputs
|
|
|
outputs = new_outputs
|
|
|
# 只打印第一张图片信息,检查是否正确计算
|
|
|
|
|
|
print("全连接处理结果:")
|
|
|
print(outputs[0])
|
|
|
print("")
|
|
|
return outputs
|
|
|
|
|
|
def nonlinear_forward(outputs):
|
|
|
# 调用设置非线性参数的函数,获取非线性参数
|
|
|
NonLPara = setnonl_para()
|
|
|
# 初始化一个空列表来存储池化后的结果
|
|
|
new_outputs = []
|
|
|
# 对outputs列表中的每个元素调用NonlinearProc函数,并将结果添加到new_outputs列表中
|
|
|
for output in outputs:
|
|
|
output, output_derivative = nonlinear_proc(output, NonLPara)
|
|
|
new_outputs.append(output)
|
|
|
# 将new_outputs赋值给outputs
|
|
|
outputs = new_outputs
|
|
|
# 只打印第一张图片信息,检查是否正确计算
|
|
|
print("非线性处理结果:")
|
|
|
print(outputs[0])
|
|
|
print("")
|
|
|
|
|
|
return outputs
|
|
|
|
|
|
def classifier_forward(outputs):
|
|
|
# 设置分类函数参数
|
|
|
ClassifyPara = setclassify_para()
|
|
|
# 初始化一个空列表来存储池化后的结果
|
|
|
new_outputs = []
|
|
|
global Prob_images
|
|
|
Prob_images = []
|
|
|
# 定义softmax函数
|
|
|
def softmax(x):
|
|
|
# 减去最大值,防止数值溢出
|
|
|
x -= np.max(x)
|
|
|
# 计算指数和归一化
|
|
|
return np.exp(x) / np.sum(np.exp(x))
|
|
|
|
|
|
# 对outputs列表中的每个元素调用ClassifierProc函数,并将结果添加到new_outputs列表中
|
|
|
for output in outputs:
|
|
|
new_outputs.append(classifier_proc(output, ClassifyPara))
|
|
|
# 调用softmax函数,得到概率分布向量
|
|
|
prob = softmax(output)
|
|
|
# 将概率向量结果存储到列表
|
|
|
Prob_images.append(prob)
|
|
|
# 将new_outputs赋值给outputs
|
|
|
outputs = new_outputs
|
|
|
# 只打印第一张图片信息,检查是否正确计算
|
|
|
print("输出示例概率向量:")
|
|
|
print(Prob_images[0:3])
|
|
|
print("")
|
|
|
|
|
|
return outputs
|
|
|
# 前向传播
|
|
|
def forward_propagation(Train_images):
|
|
|
images = Train_images# 获取训练集
|
|
|
outputs = images# 存储每一层的输出结果的列表
|
|
|
Counts = {}# 根据前向传播顺序列表,依次进行卷积层、池化层、全连接层、非线性层和分类层的操作
|
|
|
for layer in Items3[1:]:
|
|
|
if layer not in Counts:# 如果layer是第一次出现,就把它加入字典,并设置计数器为1
|
|
|
Counts[layer] = 1
|
|
|
else:# 如果layer已经出现过,就把它的计数器加一
|
|
|
Counts[layer] += 1
|
|
|
if layer == 1:# 如果是卷积层,就调用ConvProc函数,根据卷积核和步长进行卷积计算,并更新输出结果
|
|
|
outputs = conn_forward(Counts,layer,outputs)
|
|
|
elif layer == 2:# 如果是池化层,就调用PoolProc函数,进行池化计算,并更新输出结果
|
|
|
outputs = pool_forward(Counts,layer,outputs)
|
|
|
elif layer == 3:# 如果是全连接层,就调用FullConnProc函数,根据权重矩阵和偏置向量进行全连接计算,并更新输出结果
|
|
|
outputs= fullconn_forward(Counts,layer,outputs)
|
|
|
elif layer == 4: # 如果是非线性层,就调用NonlinearProc函数,根据激活函数进行非线性变换,并更新输出结果
|
|
|
outputs = nonlinear_forward(outputs)
|
|
|
elif layer == 5:# 如果是分类层,就调用ClassifierProc函数,根据权重矩阵和偏置向量进行分类计算,并更新输出结果
|
|
|
outputs=classifier_forward(outputs)
|
|
|
outputs = np.array(outputs)# 将输出结果列表转换为数组形式,方便后续处理
|
|
|
return outputs# 返回输出结果数组
|
|
|
|
|
|
def conv_one(loss,images):
|
|
|
global Conv1
|
|
|
kernel = Conv1[5][1]["kernel"]
|
|
|
bias = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel, new_bias = update_conv_layer(images, kernel, bias, loss)
|
|
|
Conv1[5][1]["kernel"] = new_kernel
|
|
|
Conv1[5][1]["bias"] = new_bias
|
|
|
|
|
|
def conv_two(loss, images):
|
|
|
global Conv1
|
|
|
global Conv2
|
|
|
kernel1 = Conv1[5][1]["kernel"]
|
|
|
bias1 = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel1, new_bias1 = update_conv_layer(images, kernel1, bias1, loss)
|
|
|
Conv1[5][1]["kernel"] = new_kernel1
|
|
|
Conv1[5][1]["bias"] = new_bias1
|
|
|
|
|
|
kernel2 = Conv2[5][2]["kernel"]
|
|
|
bias2 = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel2, new_bias2 = update_conv_layer(Pool1[-1], kernel2, bias2, loss)
|
|
|
Conv2[5][2]["kernel"] = new_kernel2
|
|
|
Conv2[5][2]["bias"] = new_bias2
|
|
|
|
|
|
def conv_three(loss, images):
|
|
|
global Conv1
|
|
|
global Conv2
|
|
|
global Conv3
|
|
|
kernel1 = Conv1[5][1]["kernel"]
|
|
|
bias1 = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel1, new_bias1 = update_conv_layer(images, kernel1, bias1, loss)
|
|
|
Conv1[5][1]["kernel"] = new_kernel1
|
|
|
Conv1[5][1]["bias"] = new_bias1
|
|
|
kernel2 = Conv2[5][2]["kernel"]
|
|
|
bias2 = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel2, new_bias2 = update_conv_layer(Pool1[-1], kernel2, bias2, loss)
|
|
|
Conv2[5][2]["kernel"] = new_kernel2
|
|
|
Conv2[5][2]["bias"] = new_bias2
|
|
|
kernel3 = Conv3[5][3]["kernel"]
|
|
|
bias3 = 0.5
|
|
|
loss = np.array([[loss]])
|
|
|
new_kernel3, new_bias3 = update_conv_layer(Pool2[-1], kernel3, bias3, loss)
|
|
|
Conv3[5][3]["kernel"] = new_kernel3
|
|
|
Conv3[5][3]["bias"] = new_bias3
|
|
|
|
|
|
def determine(Train_list,Items3):
|
|
|
if len(Train_list) == 0:
|
|
|
messagebox.showerror("错误", "请设置训练参数!")
|
|
|
return 0
|
|
|
if len(Items3) == 0:
|
|
|
messagebox.showerror("错误", "请添加图元!")
|
|
|
return 0
|
|
|
return 1
|
|
|
def label_array(i):
|
|
|
# 读取标签数据
|
|
|
df = pd.read_csv('data/emotion.csv', header=None, skiprows=range(0, i * batch), nrows=(i + 1) * batch - i * batch)
|
|
|
# 将标签数据转化成数组
|
|
|
right_label = df.iloc[:, 0].tolist()
|
|
|
right_label = list(map(int, right_label))
|
|
|
right_label = [x + 1 for x in right_label]
|
|
|
return right_label
|
|
|
|
|
|
def train():# 训练
|
|
|
if determine(Train_list, Items3) == 0:
|
|
|
return
|
|
|
epoch,rate,batch_size = Train_list
|
|
|
first_epoch = 1
|
|
|
while first_epoch <= epoch:
|
|
|
first_epoch = first_epoch + 1
|
|
|
for i in range(len(Train_images) // batch_size):
|
|
|
images = Train_images[i * batch_size:(i + 1) * batch_size]
|
|
|
forward_propagation(images)
|
|
|
global Prob_images
|
|
|
Prob_images = np.squeeze(Prob_images) # 获取分类结果的概率向量数组
|
|
|
global batch
|
|
|
batch = len(Prob_images)
|
|
|
right_label = label_array(i) # 读取标签数据,将标签数据转化成数组
|
|
|
ErrorPara = seterror_para() # 设置误差参数
|
|
|
loss = error_proc(Prob_images, right_label, ErrorPara) * 0.1 # 计算误差值
|
|
|
print("当前epoch:",first_epoch,i * batch_size,":",(i + 1) * batch_size,"误差值:",loss)
|
|
|
print("")
|
|
|
if Conv2 and Conv3 is None:
|
|
|
conv_one(loss, images)
|
|
|
elif Conv2 is not None and Conv3 is None:
|
|
|
conv_two(loss, images)
|
|
|
elif Conv3 is not None:
|
|
|
conv_three(loss, images)
|
|
|
# 输出模型程序
|
|
|
def model_show():
|
|
|
print(Items3)
|
|
|
|
|
|
def connecting_lines(obj_x, obj_y, index, x, y, x1, x2, y1, y2, y3 , image, text, smooth, width):
|
|
|
# 创建图元对象
|
|
|
Viewcanvas.create_image(obj_x+(index + x) * 80, obj_y, image=image)
|
|
|
# 创建图元对象的标签
|
|
|
Viewcanvas.create_text(obj_x+(index + x) * 80, obj_y + y1, text=text, font=("黑体", 8))
|
|
|
# 创建数据线箭头
|
|
|
Viewcanvas.create_line(obj_x+(index + x) * 80 +x1, obj_y + y2, obj_x+(index + y) * 80 +x2, obj_y + y3, arrow=tk.LAST,
|
|
|
arrowshape=(16, 20, 4), fill='lightblue', smooth=smooth, width=width)
|
|
|
|
|
|
def number_judgments(lsts):
|
|
|
global Items3
|
|
|
count = []
|
|
|
sequence = []
|
|
|
for index,lll in enumerate(lsts):
|
|
|
if type(lll) == list:
|
|
|
count.append(lll[1])
|
|
|
sequence.append(index)
|
|
|
if lll[1]==7:
|
|
|
break
|
|
|
count_2,count_3,count_4=0,0,0
|
|
|
list_count=[]
|
|
|
for i in count:
|
|
|
if i == 2:
|
|
|
count_2 += 1
|
|
|
list_count.append(i*10+count_2)
|
|
|
elif i == 3:
|
|
|
count_3 += 1
|
|
|
list_count.append(i * 10 + count_3)
|
|
|
elif i == 4:
|
|
|
count_4 += 1
|
|
|
list_count.append(i * 10 + count_4)
|
|
|
else:
|
|
|
list_count.append(i * 10)
|
|
|
Items3 = [0]
|
|
|
for i in count:
|
|
|
Items3.append(i - 1)
|
|
|
return count,list_count,sequence
|
|
|
def load_file():
|
|
|
filename = filedialog.askopenfilename(filetypes=[("Pickle files", "*.pkl")])
|
|
|
# 从本地文件中读取两个列表,用rb模式打开文件
|
|
|
with open(filename, "rb") as f:
|
|
|
# 创建一个空列表,用来存储读取的列表
|
|
|
lsts = []
|
|
|
# 用一个循环来读取所有的列表,直到文件结束
|
|
|
while True:
|
|
|
try:
|
|
|
# 读取一个列表,并追加到大列表中
|
|
|
lst = pickle.load(f)
|
|
|
lsts.append(lst)
|
|
|
except EOFError:
|
|
|
# 如果遇到文件结束的错误,就跳出循环
|
|
|
break
|
|
|
return lsts
|
|
|
|
|
|
def connecting(obj_x, obj_y, index, x, y1, image, text):
|
|
|
# 创建图元对象
|
|
|
Viewcanvas.create_image(obj_x+(index + x) * 80, obj_y, image=image)
|
|
|
# 创建图元对象的标签
|
|
|
Viewcanvas.create_text(obj_x+(index + x) * 80, obj_y + y1, text=text, font=("黑体", 8))
|
|
|
# 加载已有模型
|
|
|
def load_model():
|
|
|
import copy
|
|
|
lsts = load_file()
|
|
|
DataPara = setload_data()
|
|
|
global Train_images
|
|
|
global Test_images
|
|
|
global Viewcanvas
|
|
|
global Listinstance
|
|
|
global Viewcanvas
|
|
|
Viewcanvas.delete('all')
|
|
|
create_left_element(AllModelObj, List_image)
|
|
|
Train_images, Test_images = load_data(DataPara) # load_data()函数,根据参数加载数据集
|
|
|
# 打印一些信息,检查是否正确加载
|
|
|
print("训练集图片的形状:", Train_images.shape)
|
|
|
print("测试集图片的形状:", Test_images.shape)
|
|
|
# 根据pkl文件的前向传播顺序列表,依次识别卷积层、池化层、全连接层、非线性层和分类层的操作
|
|
|
count,counts,sequence = number_judgments(lsts)
|
|
|
x,y = 290,200
|
|
|
connecting_lines(x, y, 0, 0, 0, 25, 60, 45, 0, 0, List_image[0], '数据集', True, 1)
|
|
|
for layer in range(len(count)):
|
|
|
if count[layer] == 2: # 如果是卷积层,将读取的卷积层的信息存储到Conv实例
|
|
|
if counts[layer]%10 == 1:
|
|
|
global Conv1
|
|
|
Listinstance[1] = copy.deepcopy(lsts[sequence[layer]])
|
|
|
Conv1 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层1', True, 1)
|
|
|
if counts[layer]%10 == 2:
|
|
|
global Conv2
|
|
|
Conv2 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层2', True, 1)
|
|
|
if counts[layer]%10 == 3:
|
|
|
global Conv3
|
|
|
Conv3 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层3', True, 3)
|
|
|
if count[layer] == 3: # 如果是池化层,将读取的池化层的信息存储到Pool实例
|
|
|
if counts[layer]%10 == 1:
|
|
|
global Pool1
|
|
|
Listinstance[2] =copy.deepcopy(lsts[sequence[layer]])
|
|
|
Pool1 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化1', True, 3)
|
|
|
if counts[layer]%10 == 2:
|
|
|
global Pool2
|
|
|
Pool2 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化2', True, 3)
|
|
|
if counts[layer]%10 == 3:
|
|
|
global Pool3
|
|
|
Pool3 = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化3', True, 3)
|
|
|
if count[layer] == 4: # 如果是全连接层,将读取的全连接层的信息存储到Pool实例
|
|
|
if counts[layer]%10 == 1:
|
|
|
global Fullconn1
|
|
|
Listinstance[3] = lsts[sequence[layer]]
|
|
|
Fullconn1 = copy.deepcopy(lsts[sequence[layer]])# 深拷贝创建了一个全新的对象,要使用深拷贝,不然报错
|
|
|
Listinstance[3][5] = str(Fullconn1[5][1]["num_outputs"])
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接1', True, 3)
|
|
|
if counts[layer]%10 == 2:
|
|
|
global Fullconn2
|
|
|
Fullconn2 = lsts[sequence[layer]]
|
|
|
# FullConn[5] = FullConn[5] + " " + str(Fullconn2[5][2]["num_outputs"])
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接3', True, 3)
|
|
|
if counts[layer]%10 == 3:
|
|
|
global Fullconn3
|
|
|
Fullconn3 = lsts[sequence[layer]]
|
|
|
# FullConn[5] = FullConn[5] + " " + str(Fullconn3[5][3]["num_outputs"])
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接3', True, 3)
|
|
|
if count[layer] == 5: # 如果是非线性层,将读取的非线性层的信息存储到Nonline实例
|
|
|
Listinstance[4] = lsts[sequence[layer]]
|
|
|
connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[4], '非线性', True, 3)
|
|
|
if count[layer] == 6: # 如果是分类输出层,将读取的信息存储到Nonline实例
|
|
|
Listinstance[5] = lsts[sequence[layer]]
|
|
|
connecting(x, y, layer, 1, 45,List_image[5], '分类')
|
|
|
if count[layer] == 7: # 如果是计算误差层,将读取的信息存储到Error实例
|
|
|
Listinstance[6] = lsts[sequence[layer]]
|
|
|
connecting(x - 30, y - 120, layer, 0, 45, List_image[6], '计算误差')
|
|
|
Viewcanvas.create_line(x + 5 * 80 + 25, y, x + 5 * 80 + 20, y - 80,
|
|
|
x + 5 * 80 - 20, y - 120, arrow=tk.LAST, arrowshape=(16, 20, 4),
|
|
|
fill='lightblue', smooth=True, width=1)
|
|
|
|
|
|
def create_window(number,count,list1,list2,list3,list4,list5,list6):
|
|
|
global Listinstance
|
|
|
if number == 1:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list1[count[number] - 1].split(",")
|
|
|
para_list = [int(x) for x in para_list]
|
|
|
window = creat_window('卷积1参数调整') # 创建一个新的窗口
|
|
|
kernel_size_entry = create_input_box(window, "卷积核大小:", para_list[0]) # 创建一个输入框,获取卷积核大小
|
|
|
stride_entry = create_input_box(window, "卷积步长:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
result1 = int(kernel_size_entry.get())
|
|
|
result2 = int(stride_entry.get())
|
|
|
new_para = str(result1)+','+str(result2)
|
|
|
Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="卷积参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 2:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list1[count[number] - 1].split(",")
|
|
|
para_list = [int(x) for x in para_list]
|
|
|
window = creat_window('卷积2参数调整') # 创建一个新的窗口
|
|
|
kernel_size_entry = create_input_box(window, "卷积核大小:", para_list[0]) # 创建一个输入框,获取卷积核大小
|
|
|
stride_entry = create_input_box(window, "卷积步长:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
result1 = int(kernel_size_entry.get())
|
|
|
result2 = int(stride_entry.get())
|
|
|
new_para = str(result1)+','+str(result2)
|
|
|
Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="卷积参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 3:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list1[count[number] - 1].split(",")
|
|
|
para_list = [int(x) for x in para_list]
|
|
|
# 创建一个输入框,获取卷积核大小
|
|
|
window = creat_window('卷积3参数调整') # 创建一个新的窗口
|
|
|
kernel_size_entry = create_input_box(window, "卷积核大小:", para_list[0]) # 创建一个输入框,获取卷积核大小
|
|
|
stride_entry = create_input_box(window, "卷积步长:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
result1 = int(kernel_size_entry.get())
|
|
|
result2 = int(stride_entry.get())
|
|
|
new_para = str(result1)+','+str(result2)
|
|
|
Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="卷积参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
# 返回窗口对象
|
|
|
return window
|
|
|
# 如果是池化层
|
|
|
if number == 2:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list2[count[number] - 1].split(",")
|
|
|
para_list[1] = int(para_list[1])
|
|
|
window = creat_window('池化1参数调整') # 创建一个新的窗口
|
|
|
pool_type_combobox = create_dropdown_box(window, "池化池类型:", ["max", "avg", "min"]) # 创建一个下拉框,用于选择池化类型
|
|
|
pool_size_entry = create_input_box(window, "池化池大小:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 从下拉框中获取池化类型
|
|
|
pool_type = pool_type_combobox.get()
|
|
|
# 从输入框中获取池大小
|
|
|
pool_size = int(pool_size_entry.get())
|
|
|
# 关闭窗口
|
|
|
new_para = str(pool_type)+','+str(pool_size)
|
|
|
Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="池化参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 2:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list2[count[number] - 1].split(",")
|
|
|
para_list[1] = int(para_list[1])
|
|
|
window = creat_window('池化2参数调整') # 创建一个新的窗口
|
|
|
pool_type_combobox = create_dropdown_box(window, "池化池类型:", ["max", "avg", "min"]) # 创建一个下拉框,用于选择池化类型
|
|
|
pool_size_entry = create_input_box(window, "池化池大小:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 从下拉框中获取池化类型
|
|
|
pool_type = pool_type_combobox.get()
|
|
|
# 从输入框中获取池大小
|
|
|
pool_size = int(pool_size_entry.get())
|
|
|
# 关闭窗口
|
|
|
new_para = str(pool_type)+','+str(pool_size)
|
|
|
Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="池化参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 3:
|
|
|
# 获取第一层卷积层的参数
|
|
|
para_list = list2[count[number] - 1].split(",")
|
|
|
para_list[1] = int(para_list[1])
|
|
|
window = creat_window('池化3参数调整') # 创建一个新的窗口
|
|
|
pool_type_combobox = create_dropdown_box(window, "池化池类型:", ["max", "avg", "min"]) # 创建一个下拉框,用于选择池化类型
|
|
|
pool_size_entry = create_input_box(window, "池化池大小:", para_list[1]) # 创建一个输入框,获取卷积步
|
|
|
def modify_para():
|
|
|
# 从下拉框中获取池化类型
|
|
|
pool_type = pool_type_combobox.get()
|
|
|
# 从输入框中获取池大小
|
|
|
pool_size = int(pool_size_entry.get())
|
|
|
# 关闭窗口
|
|
|
new_para = str(pool_type)+','+str(pool_size)
|
|
|
Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="池化参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
# 返回窗口对象
|
|
|
return window
|
|
|
if number == 3:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取第一层全连接层的参数
|
|
|
para = list3[count[number] - 1]
|
|
|
para = int(para)
|
|
|
window = creat_window('全连接1参数调整') # 创建一个新的窗口
|
|
|
num_outputs_entry = create_input_box(window, "输出维度:", para) # 创建一个输入框,获取卷积步
|
|
|
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
num = int(num_outputs_entry.get())
|
|
|
new_para = str(num)
|
|
|
Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="全连接参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 2:
|
|
|
# 获取第一层全连接层的参数
|
|
|
para = list3[count[number] - 1]
|
|
|
para = int(para)
|
|
|
window = creat_window('全连接2参数调整') # 创建一个新的窗口
|
|
|
num_outputs_entry = create_input_box(window, "输出维度:", para) # 创建一个输入框,获取全连接
|
|
|
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
num = int(num_outputs_entry.get())
|
|
|
new_para = str(num)
|
|
|
Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="全连接参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
if count[number] == 3:
|
|
|
# 获取第一层全连接层的参数
|
|
|
para = list3[count[number] - 1]
|
|
|
para = int(para)
|
|
|
window = creat_window('全连接3参数调整') # 创建一个新的窗口
|
|
|
num_outputs_entry = create_input_box(window, "输出维度:", para) # 创建一个输入框,获取全连接
|
|
|
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
num = int(num_outputs_entry.get())
|
|
|
new_para = str(num)
|
|
|
Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="全连接参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
# 返回窗口对象
|
|
|
return window
|
|
|
if number == 4:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取第一层非线性层的参数
|
|
|
para = list3[count[number] - 1]
|
|
|
window = creat_window('非线性1参数调整') # 创建一个新的窗口
|
|
|
nonlinear_mode_combobox = create_dropdown_box(window, "非线性类型:",
|
|
|
["Sigmoid", "ReLU", "Tanh"]) # 创建一个下拉框,用于选择非线性类型
|
|
|
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
nonlinear_mode = nonlinear_mode_combobox.get()
|
|
|
new_para = str(nonlinear_mode)
|
|
|
Listinstance[4][5] = Listinstance[4][5].replace(list4[count[number] - 1], new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="非线性参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
# 返回窗口对象
|
|
|
return window
|
|
|
if number == 5:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取分类的参数
|
|
|
para = list5
|
|
|
para = float(para)
|
|
|
window = creat_window('分类参数调整') # 创建一个新的窗口
|
|
|
threshold_entry = create_input_box(window, "阈值:", para) # 创建一个输入框,用于输入阈值
|
|
|
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
threshold = threshold_entry.get()
|
|
|
new_para = str(threshold)
|
|
|
Listinstance[5][5] = Listinstance[5][5].replace(list5, new_para)
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="分类参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
# 返回窗口对象
|
|
|
return window
|
|
|
if number == 6:
|
|
|
# 把数字的出现次数加一
|
|
|
count[number] = count.get(number, 0) + 1
|
|
|
if count[number] == 1:
|
|
|
# 获取分类的参数
|
|
|
para = list6
|
|
|
window = creat_window('误差参数调整') # 创建一个新的窗口
|
|
|
loss_type_combobox = create_dropdown_box(window, "误差类型:",
|
|
|
["CEE", "MSE", "MAE"]) # 创建一个下拉框,用于选择误差类型
|
|
|
def modify_para():
|
|
|
# 获取输入框的内容
|
|
|
loss_type = loss_type_combobox.get()
|
|
|
new_para = str(loss_type)
|
|
|
Listinstance[6][5] = Listinstance[6][5].replace(list6[count[number] - 1], new_para)
|
|
|
# print(Listinstance[6])
|
|
|
# 关闭窗口
|
|
|
window.destroy()
|
|
|
|
|
|
# 创建一个按钮,文本为数字
|
|
|
button = tk.Button(window, text="误差参数修改")
|
|
|
# 绑定按钮的点击事件,使得点击后销毁窗口
|
|
|
button.config(command=modify_para)
|
|
|
# 把按钮放在窗口中
|
|
|
button.pack()
|
|
|
return window # 返回窗口对象
|
|
|
|
|
|
# 清空当前模型展示区
|
|
|
def restart_new_model():
|
|
|
# 这将在子进程中调用当前Python解释器,并传递当前文件的路径作为参数
|
|
|
subprocess.call([sys.executable, __file__])
|
|
|
|
|
|
def setpara_func(): # 图元参数配置与修改
|
|
|
global Viewcanvas
|
|
|
# 一个字典,用来记录每个数字出现的次数
|
|
|
count = {}
|
|
|
list1 = Listinstance[1][5].split()
|
|
|
list2 = Listinstance[2][5].split()
|
|
|
list3 = Listinstance[3][5].split()
|
|
|
list4 = Listinstance[4][5].split()
|
|
|
list5 = Listinstance[5][5]
|
|
|
list6 = Listinstance[6][5].split()
|
|
|
for i in Items4: # 清除所有文字标签
|
|
|
Viewcanvas.delete(i[0])
|
|
|
numbers = [x for x in Items3 if x <= 6] # 一个函数,用来创建一个带有一个数字标签的按钮的窗口
|
|
|
numbers = numbers[1:]
|
|
|
def iterate_numbers(): # 一个函数,用来遍历数字列表,依次创建窗口
|
|
|
if numbers: # 如果数字列表不为空
|
|
|
number = numbers.pop(0) # 从列表中弹出第一个数字
|
|
|
window = create_window(number, count, list1, list2, list3, list4, list5, list6)
|
|
|
window.wait_window() # 等待窗口被关闭
|
|
|
iterate_numbers() # 再次调用这个函数,创建下一个窗口
|
|
|
|
|
|
iterate_numbers() # 调用遍历数字的函数
|
|
|
'''
|
|
|
【编程16.20】编制程序:输出函数。
|
|
|
【目的及编程说明】读者通过编程16.20,可掌握图片分类过程。该函数的输入为1000幅图像的集合,输出为模型结构(模型的参数)。
|
|
|
基本实践内容:
|
|
|
(1)定义输入数据格式,即图像数据的维度和格式,然后加载数据集对象,使用加载数据集对象的ProcFunc将图像数据加载到网络模型中。
|
|
|
(2)使用SetParaFunc函数对每个节点进行参数赋值,通过AllModelConn表中的连接信息,使用训练好的神经网络进行预测。
|
|
|
(3)计算预测误差:使用AllModelObj表中的ProcFunc函数对预测结果与真实标签进行比较,计算误差。
|
|
|
(4)进行网络优化:使用AllModelObj表中的ProcFunc函数对误差进行反向传播,并调整神经网络中的参数和连接权重,以最小化误差。
|
|
|
(5)使用AllModelObj表中的ProcFunc函数对1000幅图像数据进行分类,得到最终的分类结果。
|
|
|
'''
|
|
|
|
|
|
def count_model(layer):
|
|
|
# 如果layer是第一次出现,就把它加入字典,并设置计数器为1
|
|
|
if layer not in Counts:
|
|
|
Counts[layer] = 1
|
|
|
# 如果layer已经出现过,就把它的计数器加一
|
|
|
else:
|
|
|
Counts[layer] += 1
|
|
|
# 如果是卷积层
|
|
|
if layer == 1:
|
|
|
if Counts[layer] == 1:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Conv1, f)
|
|
|
elif Counts[layer] == 2:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Conv2, f)
|
|
|
elif Counts[layer] == 3:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Conv3, f)
|
|
|
# 如果是池化层
|
|
|
if layer == 2:
|
|
|
if Counts[layer] == 1:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Pool1, f)
|
|
|
elif Counts[layer] == 2:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Pool2, f)
|
|
|
elif Counts[layer] == 3:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Pool3, f)
|
|
|
# 如果是全连接层
|
|
|
if layer == 3:
|
|
|
if Counts[layer] == 1:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Fullconn1, f)
|
|
|
elif Counts[layer] == 2:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Fullconn2, f)
|
|
|
elif Counts[layer] == 3:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Fullconn3, f)
|
|
|
# 如果是非线性处理层
|
|
|
if layer == 4:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Listinstance[4], f)
|
|
|
# 如果是输出分类层
|
|
|
if layer == 5:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Listinstance[5], f)
|
|
|
# 如果是计算误差层
|
|
|
if layer == 6:
|
|
|
with open("list.pkl", "ab") as f:
|
|
|
pickle.dump(Listinstance[6], f)
|
|
|
|
|
|
# 保存网络模型
|
|
|
def save_model():
|
|
|
if len(Items3) == 0:
|
|
|
messagebox.showerror("错误", "请添加图元!")
|
|
|
return
|
|
|
# 生成试验数据集,用于保存模型
|
|
|
Test_images = np.random.rand(100, 48, 48) * 0.01
|
|
|
# 对试验数据集进行前向传播的计算
|
|
|
print(forward_propagation(Test_images))
|
|
|
# 打开一个本地文件,用w模式或者wb模式
|
|
|
with open("model.pkl", "wb") as f:
|
|
|
pickle.dump(Items3, f)# 保存模型
|
|
|
# 遍历网络结构
|
|
|
for layer in Items3[1:]:
|
|
|
count_model(layer)
|
|
|
messagebox.showinfo("成功", "保存正确,恭喜!")
|
|
|
|
|
|
def check_connection():
|
|
|
connection = [x for x in Items3 if x < 6]
|
|
|
length = len(connection)
|
|
|
if length == 0:
|
|
|
messagebox.showerror("错误", "请添加图元!")
|
|
|
return
|
|
|
for i in range(length-1):
|
|
|
right_model_x = connection[i]
|
|
|
right_model_y = connection[i+1]
|
|
|
if RIGHT_MODEL[right_model_x][right_model_y] == 0:
|
|
|
messagebox.showerror("错误", "连接不正确,请检查!")
|
|
|
return
|
|
|
messagebox.showinfo("成功", "连接正确,恭喜!")
|
|
|
|
|
|
def redirector(inputStr):
|
|
|
global Text
|
|
|
Text.configure(state="normal") # 设置文本框为可编辑状态
|
|
|
Text.insert("end", inputStr) # 在文本框末尾插入内容
|
|
|
Text.see("end") # 滚动到末尾
|
|
|
Text.configure(state="disabled") # 设置文本框为只读状态
|
|
|
|
|
|
def close_program():
|
|
|
sys.exit()
|
|
|
|
|
|
def button_link():
|
|
|
global button
|
|
|
# 替每个主界面的按钮绑定各自的函数
|
|
|
button['button1'].config(command=train_setting)
|
|
|
button['button2'].config(command=train) # 训练
|
|
|
button['button3'].config(command=model_show) # 训练
|
|
|
button['button4'].config(command=load_model) # 训练
|
|
|
button['button5'].config(command=restart_new_model) # 训练
|
|
|
button['button6'].config(command=setpara_func) # 训练
|
|
|
button['button7'].config(command=save_model) # 保存模型
|
|
|
button['button8'].config(command=check_connection)
|
|
|
button['button9'].config(command=close_program)
|
|
|
|
|
|
def main():
|
|
|
# X1-X2编制赋值程序及输出界面
|
|
|
# 创建一个主窗口
|
|
|
global Root
|
|
|
global Listinstance
|
|
|
global List_image
|
|
|
global AllModelObj
|
|
|
global button
|
|
|
Root = tk.Tk()
|
|
|
# 设置窗口的大小为1200*750
|
|
|
window_width = 800 # 窗口的宽度
|
|
|
window_height = 450 # 窗口的高度
|
|
|
Root.title("神经网络可视化")
|
|
|
Root.geometry("800x450") # 设置窗口的大小和位置
|
|
|
picture_frame(window_width, window_height)
|
|
|
Listinstance = create_instance()
|
|
|
# 创建网络对象总表和网络连接对象总表
|
|
|
AllModelObj = [Listinstance[0], Listinstance[1], Listinstance[2], Listinstance[3], Listinstance[4], Listinstance[5],
|
|
|
Listinstance[6], Listinstance[7], Listinstance[8], Listinstance[9]]
|
|
|
|
|
|
img_path = ["img/data.png", "img/conv.png", "img/pool.png", "img/full_connect.png", "img/nonlinear.png",
|
|
|
"img/classifier.png", "img/error.png", "img/adjust.png", "img/arrow1.png", "img/arrow2.png"]
|
|
|
List_image = []
|
|
|
for path in img_path:
|
|
|
List_image.append(element(path))
|
|
|
button = push_button()
|
|
|
create_left_element(AllModelObj, List_image)
|
|
|
button_link()
|
|
|
sys.stdout.write = redirector
|
|
|
Root.mainloop()
|
|
|
if __name__ == '__main__':
|
|
|
main() |