You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1978 lines
90 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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
import ttkbootstrap as ttk
from tkinter import messagebox
import tkinter.scrolledtext as st
from PIL import Image, ImageTk
from ttkbootstrap import *
from tkinter import Canvas, Frame, BOTH, PhotoImage, filedialog
from X4 import setload_data
from X3 import 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 = [] # 存储卷积图元的坐标
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", ".", 120, 70).output()
Conv = ModelObj("Conv", 2, "卷积", "ConvProc", "SetConvPara", ".", 280, 70).output()
Pool = ModelObj("Pool", 3, "最大池化", "MaxPoolProc", "SetPollPara", ".", 120, 165).output()
FullConn = ModelObj("FullConn", 4, "全连接", "FullConnProc", "SetFullConnPara", ".", 280, 165).output()
Nonline = ModelObj("Nonline", 5, "非线性函数", "NonlinearProc", "SetNonLPara", ".", 120, 260).output()
Classifier = ModelObj("Classifier", 6, "分类", "ClassifierProc", "SetClassifyPara", ".", 280, 260).output()
Error = ModelObj("Error", 7, "误差计算", "ErrorProc", "SetErrorPara", ".", 120, 355).output()
AjConv = ModelObj("AjConv", 8, "卷积调整", "AjConvProc", "SetAjConvPara", ".", 280, 355).output()
AjFullconn = ModelObj("AjFullconn", 9, "全连接调整", "AjFullconnProc", "SetAjFCPara", ".", 120, 450).output()
AjNonline = ModelObj("AjNonline", 10, "非线性调整", "AjNonlineProc", "SetAjNLPara", ".", 280, 450).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-50 # 第一个矩形框的宽度
rect1_height = window_height * 3 / 4 - gap * 2-50 # 第一个矩形框的高度
rect2_x = window_width * 1 / 3 + gap - 50 # 第二个矩形框的左上角x坐标
rect2_y = gap # 第二个矩形框的左上角y坐标
rect2_width = window_width * 2 / 3 - gap +50 # 第二个矩形框的宽度
rect2_height = window_height * 3 / 4 - gap * 2 -50 # 第二个矩形框的高度
rect3_x = gap # 第三个矩形框的左上角x坐标
rect3_y = window_height * 3 / 4 + gap -50 # 第三个矩形框的左上角y坐标
rect3_width = window_width - gap * 2 # 第三个矩形框的宽度
rect3_height = window_height * 1 / 4 - gap * 2 +50 # 第三个矩形框的高度
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=1160, 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((70, 60))
# 把Image对象转换成PhotoImage对象
img = ImageTk.PhotoImage(img)
# 保存图片的引用,防止被垃圾回收
Root.img = img
return img
def push_button():
button = {}
# 指定按钮主题为 litera
style = Style(theme='litera')
# 设置blue.TButton样式的背景色为亮蓝灰前景色为白色
style.configure("blue.TButton", background="LightSlateGray", foreground="white", font=("黑体", 13))
style.map("blue.TButton",
background=[('active', 'PowderBlue'), ('pressed', 'red')], # 设置激活状态下的背景色为粉蓝色,按下状态下的背景色为红色
foreground=[('active', 'black'), ('pressed', 'white')]) # 设置激活状态下的前景色为黑色,按下状态下的前景色为白色
# 设置green.TButton样式的背景色为中海洋绿前景色为白色
style.configure("green.TButton", background="MediumSeaGreen", foreground="white", font=("黑体", 13))
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=500)
button['button1'] = button1
button2 = ttk.Button(Root, text=" 训练 ", style="blue.TButton")
button2.place(x=190, y=500)
button['button2'] = button2
button3 = ttk.Button(Root, text=" 输出模型程序 ", style="blue.TButton")
button3.place(x=300, y=500)
button['button3'] = button3
# 创建五个按钮,用于触发操作
button4 = ttk.Button(Root, text=" 加载已有模型 ", style="green.TButton")
button4.place(x=370, y=5)
button['button4'] = button4
button5 = ttk.Button(Root, text=" 新建模型 ", style="green.TButton")
button5.place(x=530, y=5)
button['button5'] = button5
button6 = ttk.Button(Root, text=" 图元参数设置 ", style="green.TButton")
button6.place(x=660, y=5)
button['button6'] = button6
button7 = ttk.Button(Root, text=" 保存模型 ", style="green.TButton")
button7.place(x=820, y=5)
button['button7'] = button7
button8 = ttk.Button(Root, text=" 模型正确性检查 ", style="green.TButton")
button8.place(x=940, y=5)
button['button8'] = button8
button9 = ttk.Button(Root, text="退出", style="green.TButton")
button9.place(x=1120, y=5)
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(',')
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):
# 输入输入特征图imagePoolPara参数字典
# 输出:池化后特征图(数组)
# 从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 ErrorCEE
# 从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 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 update_parameters(weight_matrix, bias_vector, loss):
# 根据激活函数的参数选择相应的函数和导数
# 计算权重矩阵和偏置向量的梯度,使用链式法则
learning_rate = 0.01
gradient_weights = np.outer(loss, learning_rate)
# 更新权重矩阵和偏置向量
weight_matrix = weight_matrix - gradient_weights
bias_vector = bias_vector - learning_rate * bias_vector
# 返回更新后的权重矩阵和偏置向量
return weight_matrix, bias_vector
def fully_one(loss):
global Fullconn1
weight1 = Fullconn1[5][1]["weights"]
bias1 = Fullconn1[5][1]["bias"]
loss = np.array([loss])
new_weight1, new_bias1 = update_parameters(weight1, bias1, loss)
Fullconn1[5][1]["weights"] = new_weight1
Fullconn1[5][1]["bias"] = new_bias1
def fully_two(loss):
global Fullconn1
global Fullconn2
weight1 = Fullconn1[5][1]["weights"]
bias1 = Fullconn1[5][1]["bias"]
loss = np.array([loss])
new_weight1, new_bias1 = update_parameters(weight1, bias1, loss)
Fullconn1[5][1]["weights"] = new_weight1
Fullconn1[5][1]["bias"] = new_bias1
weight2 = Fullconn2[5][1]["weights"]
bias2 = Fullconn2[5][1]["bias"]
new_weight2, new_bias2 = update_parameters(weight2, bias2, loss)
Fullconn2[5][1]["weights"] = new_weight2
Fullconn2[5][1]["bias"] = new_bias2
def fully_three(loss):
global Fullconn1
global Fullconn2
global Fullconn3
weight1 = Fullconn1[5][1]["weights"]
bias1 = Fullconn1[5][1]["bias"]
loss = np.array([loss])
new_weight1, new_bias1 = update_parameters(weight1, bias1, loss)
Fullconn1[5][1]["weights"] = new_weight1
Fullconn1[5][1]["bias"] = new_bias1
weight2 = Fullconn2[5][1]["weights"]
bias2 = Fullconn2[5][1]["bias"]
new_weight2, new_bias2 = update_parameters(weight2, bias2, loss)
Fullconn2[5][1]["weights"] = new_weight2
Fullconn2[5][1]["bias"] = new_bias2
weight3 = Fullconn3[5][3]["weights"]
bias3 = Fullconn3[5][1]["bias"]
new_weight3, new_bias3 = update_parameters(weight3, bias3, loss)
Fullconn3[5][3]["weights"] = new_weight3
Fullconn3[5][3]["bias"] = new_bias3
# 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 = creat_window("全连接参数配置")
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 = creat_window('分类参数配置') # 创建弹出窗口
# 创建一个下拉框,用于选择非线性类型
class_box_combobox = create_input_box(top, "阈值:", 0.001) # 创建一个下拉框,用于选择池化类型
# 创建
def get_input():
# 从输入框中获取阈值
threshold = float(class_box_combobox.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 - 70, obj_y + y, text=text, font=("黑体", 14))
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(count,layer,outputs):
# 若为第一层卷积层且该层的图元实例Conv1还未创建
if count[layer] == 1 and Conv1 == None:
# 调用SetConvPara()函数获取卷积层1参数
ConvPara = setconv_para(1)
# 若图元实例Conv1已经被定义说明需要根据反向传播调整卷积核
if count[layer] == 1 and Conv1 != None:
ConvPara = Conv1[5][1]
if count[layer] == 2 and Conv2 == None:
ConvPara = setconv_para(count[layer])
if count[layer] == 2 and Conv2 != None:
ConvPara = Conv2[5][2]
if count[layer] == 3 and Conv3 == None:
ConvPara = setconv_para(count[layer])
if count[layer] == 3 and Conv3 != 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(count,layer,outputs):
global Pool1
global Pool2
global Pool3
if count[layer] == 1 and Pool1 == None:
# 调用设置池化层参数的函数,获取池化参数
PoolPara = setpool_para(count[layer])
if count[layer] == 1 and Pool1 != None:
PoolPara = Pool1[5][1]
if count[layer] == 2 and Pool2 == None:
PoolPara = setpool_para(count[layer])
if count[layer] == 2 and Pool2 != None:
PoolPara = Pool2[5][2]
if count[layer] == 3 and Pool3 == None:
PoolPara = setpool_para(count[layer])
if count[layer] == 3 and Pool3 != 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 count[layer] == 1:
Pool1.append(outputs)
if count[layer] == 2:
Pool2.append(outputs)
if count[layer] == 3:
Pool3.append(outputs)
print("池化处理结果:")
print(outputs[0])
print("")
return outputs
def fullconn_forward(count,layer,outputs):
output = outputs[0]
# 若图元实例Fullconn1未被定义
if count[layer] == 1 and Fullconn1 == None:
# 调用设置全连接层参数的函数,获取全连接参数
FullConnPara = setfullconn_para(output, count[layer])
# 若图元实例Fullconn1已经被定义说明需要根据反向传播调整权重矩阵和偏置向量
if count[layer] == 1 and Fullconn1 != None:
FullConnPara = Fullconn1[5][1]
if count[layer] == 2 and Fullconn2 == None:
FullConnPara = setfullconn_para(output, count[layer])
if count[layer] == 2 and Fullconn2 != None:
FullConnPara = Fullconn2[5][2]
if count[layer] == 3 and Fullconn3 == None:
FullConnPara = setfullconn_para(output, count[layer])
if count[layer] == 3 and Fullconn3 != 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 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 == None) and (Conv3 == None):
conv_one(loss,images)
elif (Conv2 != None) and (Conv3 == None):
conv_two(loss,images)
elif (Conv3 != None):
conv_three(loss,images)
if (Fullconn2 == None) and (Fullconn3 == None):
fully_one(loss)
elif (Fullconn2 != None) and (Fullconn3 == None):
fully_two(loss)
elif (Fullconn3 != None):
fully_three(loss)
# 输出模型程序
def model_show():
pixel = ['数据集','卷积','池化','全连接','非线性','分类','误差','调整1','调整2','调整3']
pixel_items3 = []
for i in Items3:
pixel_items3.append(pixel[i])
print(pixel_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) * 110, obj_y, image=image)
# 创建图元对象的标签
Viewcanvas.create_text(obj_x+(index + x) * 110, obj_y + y1, text=text, font=("黑体", 14))
# 创建数据线箭头
Viewcanvas.create_line(obj_x+(index + x) * 110 +x1, obj_y + y2, obj_x+(index + y) * 110 +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) * 110, obj_y, image=image)
# 创建图元对象的标签
Viewcanvas.create_text(obj_x+(index + x) * 110, obj_y + y1, text=text, font=("黑体", 14))
# 加载已有模型
def load_model():
import copy
lsts = load_file()
DataPara = setload_data()
global Train_images
global Test_images
global Viewcanvas
global Listinstance
global Viewcanvas
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 = 420,300
connecting_lines(x, y, 0, 0, 0, 25, 60, 45, 0, 0, List_image[0], '数据集', True, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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, 2)
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 + layer * 110 + 25, y, x + layer * 110 + 10, y - 60,
x + 5 * 110 - 20, y - 120, arrow=tk.LAST, arrowshape=(16, 20, 4),
fill='lightblue', smooth=True, width=2)
# 创建图元对象
Viewcanvas.create_image(600, 180, image=List_image[7])
# 创建图元对象的标签
Viewcanvas.create_text(600, 230, text='调整1', font=("黑体", 14))
Viewcanvas.create_line(x + layer * 110 -50, y - 140, 630, 165,arrow=tk.LAST, arrowshape=(16, 20, 4),
fill='lightblue', smooth=True, width=2)
Viewcanvas.create_line(600, 200, x + 1 * 110 ,y-20, arrow=tk.LAST, arrowshape=(16, 20, 4),
fill='lightblue', smooth=True, width=2)
# 创建图元对象
Viewcanvas.create_image(860, 200, image=List_image[7])
# 创建图元对象的标签
Viewcanvas.create_text(860, 250, text='调整2', font=("黑体", 14))
Viewcanvas.create_line(x + layer * 110 -60, y - 120, 875, 200,arrow=tk.LAST, arrowshape=(16, 20, 4),
fill='lightblue', smooth=True, width=2)
Viewcanvas.create_line(835, 200, x + (layer-2) * 110 + 10, y - 20, arrow=tk.LAST, arrowshape=(16, 20, 4),
fill='lightblue', smooth=True, width=2)
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
file_path = 'list.pkl'
# 检查文件是否存在
if os.path.exists(file_path):
# 删除文件
os.remove(file_path)
# 生成试验数据集,用于保存模型
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 = 1200 # 窗口的宽度
window_height = 750 # 窗口的高度
Root.title("神经网络可视化")
Root.geometry("1200x750") # 设置窗口的大小和位置
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()