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.

587 lines
33 KiB

8 months ago
import os
import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog
from PIL import Image
from X1 import image_to_array
'''
编程16.6编制程序设置加载数据集的参数SetLoadData()及加载数据集LoadData()
目的及编程说明读者通过编程16.6可掌握读取文件及数据预处理的相关方法SetLoadData()设置加载数据集的相关参数
LoadData()按所设置的参数读取数据集SetDataPara函数实现的具体要求为
1确定参数信息如数据集路径信息图片大小每批次读入图片的数量等
2返回DataPara参数
LoadData函数实现的具体要求为
1根据路径信息读入图片
2将图片缩放为固定大小
3将图片转换为数组的形式
4将数组中具体数值归一化
5返回转换后的数组
'''
# 定义一个函数,实现图片转换为数组的功能
def image_to_array(path,height,width):
img = Image.open(path).convert("L") #转换为灰度模式
img = img.resize((height, width)) # 将图片缩放为height*width的固定大小
data = np.array(img) # 将图片转换为数组格式
data = data / 255.0 # 将数组中的数值归一化除以255
return data
def setload_data(): # 定义加载数据集的参数SetLoadData()
# 设置数据集路径信息
train_imgPath = 'data/train/' # 训练集文件夹的位置
test_imgPath = 'data/verify/' # 测试集文件夹的位置
img_width = 48 # 图片宽度
img_height = 48 # 图片高度
# 设置每批次读入图片的数量
batch_size = 32 # 批次大小
# 返回DataPara参数这里用一个字典来存储
DataPara = {"train_imgPath": train_imgPath,
"test_imgPath": test_imgPath,
"img_width": img_width,
"img_height": img_height,
"batch_size": batch_size}
return DataPara
# if __name__ == '__main__':
# root = tk.Tk()
# root.withdraw()
# DataPara = setload_data()
# print(DataPara)
# 定义加载数据集load_data()
def load_data(DataPara):
imgPaths=["train_imgPath","test_imgPath"] # DataPara的键名
listimages=[] # 存储图片的列表
list_path = []
for datapath in imgPaths:
filenames = os.listdir(DataPara[datapath])
Path = []
for tfname in filenames:
if os.path.isdir(tfname) == False: # 如果不是子文件夹,就是图片文件
path = os.path.join(DataPara[datapath], tfname) # 使用os.path.join()拼接图片路径
Path.append(path)
file_paths = sorted(Path, key=lambda x: int(x.split("/")[-1].split(".")[0]))
list_path.append(file_paths)
for data_path in list_path:
images = [] # 存储图片的列表
for path in data_path:
# print(path)
img = image_to_array(path, DataPara["img_width"], DataPara["img_height"]) # 读取图片数据
img = img.T # 转置,图像的行和列将互换位置。
images.append(img) # 将图片数组添加到训练集图片列表中
listimages.append(np.array(images)) # 返回转换后的数组
return listimages[0],listimages[1]
# if __name__ == '__main__':
# root = tk.Tk()
# root.withdraw()
# DataPara = setload_data()
# train_images, test_images = load_data(DataPara)
# print(train_images.shape)
# print(test_images.shape)
'''
编程16.7编制程序设置卷积参数SetConvPara()及卷积函数ConvProc()
目的及编程说明读者通过编程16.7可实现卷积神经网络中的重要步骤卷积它将一个卷积核应用于输入图像
以提取特征SetConvPara()设置相关参数ConvProc()按相关参数完成卷积计算
SetConvPara ()实现的具体要求为
1确定参数信息卷积核大小步长填充
2返回ConvPara参数
ConvProc() 实现的具体要求为
1获取输入数据的大小
2获取卷积核的大小
3计算输出数据的大小
4对于每一个输出位置进行卷积操作
5返回卷积计算后的数组特征向量
'''
# 定义设置卷积参数的函数SetConvPara()
def setconv_para():
kernel_h = 3 # 设置卷积核大小这里假设是3x3
kernel_w = 3
kernel = [[1.289202, -1.471377, -0.238452],# 卷积核为3x3的单位矩阵(高斯分布)通常再乘上一个小的数比如0.1
[-0.562343, -0.019988, -0.441446],
[1.627381, 1.390266, 0.812486]]
stride = 1 # 设置步长这里假设是1
padding = 0 # 设置填充这里假设是0
ConvPara = {"kernel": kernel,# 返回ConvPara参数这里用一个字典来存储
"kernel_h": kernel_h,
"kernel_w": kernel_w,
"stride": stride,
"padding": padding}
return ConvPara
# if __name__ == '__main__':
# ConvPara = setconv_para()
# print(ConvPara)
def conv_proc(image, ConvPara): # 定义卷积函数ConvProc()
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"] # 获得步长
output[k, i, j] = np.sum(image[k, i * stride:i * stride + 3, j * stride:j * stride + 3] * kernel)# 计算卷积操作
return output # 返回卷积计算后的数组(特征向量)
# if __name__ == '__main__':
# root = tk.Tk()
# root.withdraw()
# DataPara = setload_data()
# train_images, test_images = load_data(DataPara)
# ConvPara = setconv_para()
# conv_images = [] # 存储卷积处理后的图片的列表
# for image in train_images: # 获取训练集的图片数据
# dim = len(image.shape) # 获取矩阵的维度
# if dim == 2: # 如果是二维矩阵,则转化为三维矩阵
# image_h, image_w = image.shape
# image = np.reshape(image, (1, image_h, image_w))
# output = conv_proc(image, ConvPara) # 调用ConvProc()函数根据ConvPara参数完成卷积计算
# conv_images.append(output) # 将卷积结果存储到列表
# conv_images = np.array(conv_images) # 将卷积处理后的图片列表转换为数组形式,方便后续处理
# print(conv_images)
def convolutional_operation(images):
global ConvPara
# 存储卷积处理后的图片的列表
conv_images = []
# 获取训练集的图片数据
for image in images:
# 获取矩阵的维度
dim = len(image.shape)
# 如果是二维矩阵,则转化为三维矩阵
if dim == 2:
image_h, image_w = image.shape
image = np.reshape(image, (1, image_h, image_w))
# 调用ConvProc()函数根据ConvPara参数完成卷积计算
output = conv_proc(image, ConvPara)
# 将卷积结果存储到列表
conv_images.append(output)
# 若为三维矩阵,则保持不变直接卷积处理
elif dim == 3:
output = conv_proc(image, ConvPara)
conv_images.append(output)
# 将卷积处理后的图片列表转换为数组形式,方便后续处理
conv_images = np.array(conv_images)
return conv_images
'''
编程16.8编制程序设置池化参数SetPoolPara()及池化函数PoolProc()
目的及编程说明读者通过编程16.8可掌握池化函数的基本方法池化的作用是在卷积神经网络的特征提取过程中
对卷积后提取到的数组进行降维处理从而降低特征数组的维度和计算量SetPoolPara()设置相关参数
PoolProc ()按相关参数完成池化处理
SetPoolPara()实现的具体要求为
1确定参数信息池化的类型Poolmode池化窗口大小pool_size步长stride
2返回PoolPara参数
PoolProc()实现的具体要求为
1获取输入数据的形状
2获取池化窗口的大小
3创建一个输出结果数组计算池化后的结果大小
4对每个池化窗口应用池化函数判断池化类型池化模式为'max'
则使用窗口中的最大值池化模式为'avg'则使用窗口中的平均值
池化模式为'min'则使用窗口中的最小值
5返回池化计算后的数组
'''
def setpool_para(): # 定义设置池化参数的函数
pool_mode = "max" # 设置池大小和池类型这里假设是2x2最大池化
pool_size = 2
stride = 2 # 设置步长这里假设是2
PoolPara = {"pool_mode": pool_mode,"pool_size": pool_size,"stride": stride} # 返回PoolPara参数这里用一个字典来存储
return PoolPara # 返回PoolPara参数
# if __name__ == '__main__':
# PoolPara = setpool_para()
# print(PoolPara)
def pool_proc(image, PoolPara): # 定义池化函数
pool_mode = PoolPara["pool_mode"]
pool_size = PoolPara["pool_size"]
stride = PoolPara["stride"]
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 # 返回特征图。
# if __name__ == '__main__':
# ···
# PoolPara = setpool_para()
# pool_images = [] # 存储池化处理后的图片的列表
# for image in conv_images: # 获取卷积后的图片数据
# output = pool_proc(image, PoolPara)
# pool_images.append(output) # 将池化结果存储到列表
# pool_images = np.array(pool_images) # 将池化处理后的图片列表转换为数组形式,方便后续处理
# print(pool_images)
def pooling_treatment(conv_images):
global PoolPara
pool_images = [] # 存储池化处理后的图片的列表
for image in conv_images: # 获取卷积后的图片数据
output = pool_proc(image, PoolPara)
pool_images.append(output) # 将池化结果存储到列表
pool_images = np.array(pool_images) # 将池化处理后的图片列表转换为数组形式,方便后续处理
return pool_images
'''
编程16.9编制程序设置全连接参数SetFullConnPara()及全连接函数FullConnProc()
目的及编程说明读者通过编程16.9可掌握全连接处理的基本方法全连接是将前面卷积和池化层提取到的数组转换为一个一维的数组
并将其传递给后面的分类器进行分类全连接层中的每个神经元都与前一层中的所有神经元相连
因此全连接层可以将前一层中提取的所有特征信息汇总到一个向量中为后续的分类任务提供更加丰富的特征表示
全连接层具有较高的参数数量和计算量SetFullConnPara()设置全连接相关参数FullConnProc()按相关参数完成全连接操作
SetFullConnPara()实现的具体要求为
1确定参数信息: 权重矩阵偏置向量
2返回FullConnPara参数
FullConnProc() 实现的具体要求为
1对输入进行展平处理变换为单通道的二维数组格式如果输入数据为三通道需要将其转换为单通道的二维数组形式
2计算全连接层的线性变换inputdata与权重矩阵w进行乘法再加上偏置向量b
3返回全连接计算后的数组
'''
def setfullconn_para(data): # 定义一个函数来设置全连接层的相关参数,这里可以根据实际情况修改或随机生成
c, height, width = data.shape# 获取池化后的图片数组的长度和宽度
num_outputs = 10 # 输出维度为10可以设置
weights = np.random.randn(num_outputs, height * width) * 0.1 # 设置权重矩阵
bias = np.random.randn(1, num_outputs) # 偏置向量
FullConnPara = {"weights": weights, "bias": bias, "num_outputs": num_outputs}
return FullConnPara # 返回FullConnPara参数
def fullconn_proc(inputdata, FullConnPara): # 定义一个函数来完成全连接操作
weights = FullConnPara["weights"] # 从FullConnPara参数中获取权重矩阵
bias = FullConnPara["bias"] # 偏置向量
inputdata = inputdata.reshape(1, inputdata.shape[1] * inputdata.shape[2]) # 对输入进行展平处理,变换为单通道的一维数组格式
output = np.dot(inputdata, weights.T) + bias # 计算全连接层的线性变换inputdata与权重矩阵w进行乘法再加上偏置向量b
return output # 返回全连接计算后的数组
# if __name__ == '__main__':
# ···
# FullConnPara = setfullconn_para(pool_images[0])
# fullconn_images = [] # 存储全连接处理后的图片的列表
# for image in pool_images: # 获取池化后的图片数据
# output = fullconn_proc(image, FullConnPara)
# fullconn_images.append(output) # 将全连接处理后的结果存储到列表
# fullconn_images = np.array(fullconn_images) # 将全连接处理后的图片列表转换为数组形式,方便后续处理
def fullyconnected_operation(pool_images):
global FullConnPara
# 存储全连接处理后的图片的列表
fullconn_images = []
# 获取池化后的图片数据
for image in pool_images:
output = fullconn_proc(image, FullConnPara)
# 将全连接处理后的结果存储到列表
fullconn_images.append(output)
# 将全连接处理后的图片列表转换为数组形式,方便后续处理
fullconn_images = np.array(fullconn_images)
return fullconn_images
'''
编程16.10编制程序设置非线性参数SetNonLPara()及非线性函数NonlinearProc()
目的及编程说明读者通过编程16.10可掌握非线性函数使用的基本方法它的主要作用是引入非线性特性从而使网络能够学习更加复杂的特征和模式
通过激活函数将线性组合的输出进行非线性变换使得网络可以处理非线性关系和非线性变换从而更好地适应数据的复杂性
主要包含的类型常用的非线性函数包括ReLUtanh和sigmoid等非线性函数的输入数据是特征向量非线性函数应该返回应用激活函数后的特征向量
SetNonLPara()设置相关参数NonlinearProc()按相关参数完成对输入数据的非线性处理操作
SetNonLPara()实现的具体要求为
1确定参数信息:非线性函数的类型
2返回NonLPara参数
NonlinearProc(inputdataNonLPara) 实现的具体要求为
(1)判断NonLPara进行相应的计算Sigmoid\Relu\Tanh;
(2)返回计算后的值
'''
def setnonl_para(): # 定义设置非线性参数的函数
# 可以选择"Sigmoid", "ReLU" 或 "Tanh"
nonlinearmode = "ReLU" # 确定参数信息:非线性函数的类型
NonLPara = {"nonlinearmode": nonlinearmode} # 返回NonLPara参数这里用一个字典来存储
return NonLPara # 返回NonLPara参数
def nonlinear_proc(inputdata, NonLPara): # 定义非线性函数
nonlinearmode = NonLPara["nonlinearmode"] # 从NonLPara参数中获取非线性函数类型
if nonlinearmode == "Sigmoid": # 判断nonlinearmode进行相应的计算
output = 1 / (1 + np.exp(-inputdata)) # Sigmoid函数将任何实数的输入映射到0和1之间的输出
elif nonlinearmode == "ReLU":
output = np.maximum(inputdata, 0) # ReLU函数将负数输入置为0而正数输入保持不变
elif nonlinearmode == "Tanh":
output = np.tanh(inputdata) # Tanh函数将任何实数的输入映射到-1和1之间的输出
else:
raise ValueError("Invalid nonlinear mode") # 非法的非线性类型,抛出异常
return output # 返回计算后的值
# if __name__ == '__main__':
# ···
# NonLPara = setnonl_para()
# nonlinear_images = []
# for image in fullconn_images: # 获取全连接处理后的图片数据
# output = nonlinear_proc(image, NonLPara)
# nonlinear_images.append(output) # 将非线性处理后的结果存储到列表
# nonlinear_images = np.array(nonlinear_images) # 将非线性处理后的图片列表转换为数组形式,方便后续处理
def activation(fullconn_images):
global NonLPara
# 存储非线性处理后的图片的列表
nonlinear_images = []
for image in fullconn_images: # 获取全连接处理后的图片数据
output = nonlinear_proc(image, NonLPara)
# 将非线性处理后的结果存储到列表
nonlinear_images.append(output)
# 将非线性处理后的图片列表转换为数组形式,方便后续处理
nonlinear_images = np.array(nonlinear_images)
return nonlinear_images
'''
编程16.11编制程序设置分类函数参数SetClassifyPara()及分类函数ClassifierProc()
目的及编程说明读者通过编程16.11可掌握输出分类函数的基本方法
SetClassifyPara()设置相关参数ClassifierProc ()按相关参数完成对输入数据的分类标签设置
SetClassifyPara()实现的具体要求为
1设定阈值
2返回ClassifyPara参数
ClassifierProc()实现的具体要求为
1找到概率超过阈值的标签分类就是分类结果
2返回分类标签
'''
def setclassify_para(): # 定义设置分类函数参数的函数
threshold = 0.1 # 设定阈值,可以根据你的数据和任务来调整阈值
ClassifyPara = {"threshold": threshold} # 返回ClassifyPara参数这里用一个字典来存储
return ClassifyPara # 返回ClassifyPara参数
def classifier_proc(inputdata, ClassifyPara): # 定义分类函数
def softmax(x): # 定义softmax函数
x -= np.max(x) # 减去最大值,防止数值溢出
return np.exp(x) / np.sum(np.exp(x)) # 计算指数和归一化
threshold = ClassifyPara["threshold"] # 从ClassifyPara参数中获取阈值
output = -1 # 初始化输出为-1
prob = softmax(inputdata) # 调用softmax函数得到概率分布向量
prob1 = prob[prob >= threshold] # 如果概率高于阈值,就将该类别加入输出结果
index = np.where(prob == max(prob1)) # 使用where()函数来返回等于概率最大值的元素的索引
output = index[1].item(0) + 1 # 使用item()方法来将索引转换为标准Python标量
return output # 返回分类标签
# if __name__ == '__main__':
# ···
# classifier_images = [] # 存储分类处理后的图片的列表
# prob_images = [] # 存储分类处理后的概率向量
# for image in nonlinear_images: # 获取非线性处理后的图片数据
# def softmax(x): # 定义softmax函数
# x -= np.max(x) # 减去最大值,防止数值溢出
# return np.exp(x) / np.sum(np.exp(x)) # 计算指数和归一化
# prob = softmax(image) # 调用softmax函数得到概率分布向量
# prob_images.append(prob) # 将概率向量结果存储到列表
# classifypara = setclassify_para()
# output = classifier_proc(image, classifypara) # 进行分类处理
# classifier_images.append(output) # 将分类结果存储到列表
# classifier_images = np.array(classifier_images) # 将分类的结果列表转换为数组形式,方便后续处理
def classify(nonlinear_images):
global ClassifyPara
classifier_images = [] # 存储分类处理后的图片的列表
prob_images = [] # 存储分类处理后的概率向量
for image in nonlinear_images: # 获取非线性处理后的图片数据
def softmax(x): # 定义softmax函数
x -= np.max(x) # 减去最大值,防止数值溢出
return np.exp(x) / np.sum(np.exp(x)) # 计算指数和归一化
prob = softmax(image) # 调用softmax函数得到概率分布向量
prob_images.append(prob) # 将概率向量结果存储到列表
output = classifier_proc(image, ClassifyPara) # 进行分类处理
classifier_images.append(output) # 将分类结果存储到列表
classifier_images = np.array(classifier_images) # 将分类的结果列表转换为数组形式,方便后续处理
return classifier_images, prob_images
'''
编程16.12编制程序设置标签类别SetLabelPara()及为样本标记标签函数LabelProc()
目的及编程说明读者通过编程16.12为样本标记标签的过程SetLabelPara()实现的具体要求为
设置标签类别列表将标签列表转化为one-hot向量的形式
LabelProc()实现的具体要求为
1读取样本数据集遍历每个样本
2读取标签列表
3将样本和对应的标签组成元组返回标记好标签的样本列表
'''
def setlabel_para(): # 定义设置标签类别的函数
label_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 设置标签类别列表,可以根据你的数据和任务来调整
one_hot_matrix = np.eye(len(label_list)) # 创建一个单位矩阵,大小为标签类别的个数
LabelPara = (label_list, one_hot_matrix) # 返回标签类别列表和one-hot矩阵这里用一个元组来存储
return LabelPara # 返回LabelPara参数
def label_proc(nonlinear_images,prob_images, classifier_images):# 定义为样本标记标签的函数
global LabelPara
label = []
for i in range(nonlinear_images.shape[0]):
label_list = np.append(prob_images[i], classifier_images[i]) # 连接起来形成一个新的数组
label.append(label_list) # label_list添加到label列表中
LABEL = np.array(label) # 使用np.array()将label列表转换为LABEL数组
sampledata = np.array(LABEL) # 读取样本数据集,假设是一个二维数组,每一行是一个样本,每一列是一个特征
label_list, one_hot_matrix = LabelPara # 读取标签列表和one-hot矩阵假设样本的最后一列是标签值
labeled_samples = [] # 遍历每个样本,将样本和对应的标签组成元组,返回标记好标签的样本列表
for sample in sampledata:
features = sample[:-1] # 获取样本的特征部分和标签部分
label = sample[-1]
index = label_list.index(label) # 先找到标签在标签列表中的索引位置
label = np.take(one_hot_matrix, index, axis=0) # 然后从one-hot矩阵中取出对应的向量
labeled_sample = (features, label) # 将样本和标签组成元组,并添加到列表中
labeled_samples.append(labeled_sample)
return labeled_samples # 返回标记好标签的样本列表
# if __name__ == '__main__':
# ···
# classifier_images = np.array(classifier_images) # 将分类的结果列表转换为数组形式,方便后续处理
# labeled_samples = label_proc(nonlinear_images, prob_images, classifier_images)
# print(labeled_samples)
'''
编程16.13编制程序设置误差参数SetErrorPara()及计算误差函数ErrorProc()
目的及编程说明读者通过编程20.13可掌握计算误差的基本方法
计算误差的函数损失函数通常有交叉熵Cross Entropy ErrorCEE
均方误差Mean Squared ErrorMSE
平均绝对误差Mean Absolute ErrorMAE
使用损失函数计算预测值与真实值之间的误差SetErrorPara()设置相关参数ErrorProc()按相关参数完成误差相关的计算
SetErrorPara()实现的具体要求为1确定参数信息: 标签类别损失函数类型
2返回ErrorProc参数
ErrorProc() 实现的具体要求为 1将真实标签类别label转换为one-hot编码形式
2确定损失函数类别实现不同的损失函数计算输入值与label之间的误差
3返回误差值loss
'''
def seterror_para(): # 定义设置误差参数的函数
label_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 确定参数信息: 标签类别,损失函数类型
loss_type = "CEE" # 假设损失函数类型为交叉熵Cross Entropy ErrorCEE
ErrorPara = (label_list, loss_type) # 返回ErrorProc参数这里用一个元组来存储
return ErrorPara # 返回ErrorPara参数
def error_proc(input, label, ErrorPara): # 定义计算误差的函数
label_list, loss_type = ErrorPara # 读取标签列表和损失函数类型
one_hot_matrix = np.eye(len(label_list)) # 创建一个单位矩阵,大小为标签类别的个数
index = [label_list.index(l) for l in label] # 找到标签在标签列表中的索引位置
label_one_hot = np.take(one_hot_matrix, index, axis=0) # 从one-hot矩阵中取出对应的向量
if loss_type == "CEE": # 确定损失函数类别实现不同的损失函数计算输入值与label之间的误差
# 使用交叉熵损失函数,公式为:-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") # 如果损失函数类型不在以上三种中,抛出异常
return loss # 返回误差值loss
# if __name__ == '__main__':
# ···
# prob_images = np.squeeze(prob_images)
# input = prob_images
# array = np.arange(1, 11) # 定义一个一维数组包含1到10的整数
# right_label = np.random.choice(array, 900) # 从a中随机抽取100个元素有放回每个元素的概率相同
# ErrorPara = seterror_para() # 设置误差参数
# loss = error_proc(input, right_label, ErrorPara) # 计算误差值
# print(loss)
def loss_count(prob_images):
global ErrorPara
# 假设有以下输入值和真实标签值,输入值是一个概率矩阵,真实标签值是一个类别列表
prob_images = np.squeeze(prob_images)
input = prob_images # print(len(input))
array = np.arange(1, 11) # 定义一个一维数组包含1到10的整数
# 从a中随机抽取100个元素有放回每个元素的概率相同
right_label = np.random.choice(array, 900)
loss = error_proc(input, right_label, ErrorPara) # 计算误差值
return loss
'''
编程16.14编制程序设置卷积核调整函数的参数SetAjConvPara()卷积核调整函数AjConvProc()
目的及编程说明读者通过编程20.14可掌握更新卷积核的基本方法反向传播到卷积层的输出对于卷积核的每一个权重
可以计算出它对误差的贡献然后使用梯度下降算法更新该权重SetAjConvPara()设置相关参数AjConvProc()按相关参数完成卷积核调整相关的计算
SetAjConvPara()实现的具体要求为:1确定卷积调整的参数卷积核信息学习率误差值
2返回参数AjConvPara4.028404024493524
AjConvProc()的具体实现要求为1使用误差值计算卷积层的误差项和梯度2梯度下降算法更新卷积层参数3返回新的卷积层参数
'''
def setajconv_para():
kernel = np.array([[1.0,2.0],[3.0,4.0]]) # 卷积核信息
learning_rate = 0.01 # 学习率
bias = 0.5 # 偏置
loss = np.array([[0.1, 0.2], [0.3, 0.4]])
AjConvPara={'kernel_info': kernel,'learning_rate': learning_rate,'bias': bias,'loss': loss}
return AjConvPara
def ajconv_proc(input, AjConvPara):
k = [] # 计算卷积核和偏置项的梯度
bias_grad = np.sum(AjConvPara['loss']) # 计算偏置项梯度为误差值之和
for c in input:
kernel_grad = np.zeros_like(AjConvPara['kernel_info']) # 初始化卷积核梯度为零矩阵
for i in range(AjConvPara['loss'].shape[0]): # 遍历误差值矩阵的每一行
for j in range(AjConvPara['loss'].shape[1]): # 遍历误差值矩阵的每一列
kernel_grad += np.rot90(c[i:i + 2, j:j + 2], 2) * AjConvPara['loss'][i, j]
k.append(kernel_grad)
result = np.stack(k, axis=0)
kernel_grad = np.sum(result, axis=0) / len(input)
kernel = AjConvPara['kernel_info'] - AjConvPara['learning_rate'] * kernel_grad
bias = AjConvPara['bias'] - AjConvPara['learning_rate'] * bias_grad
return kernel, bias# 返回更新后的卷积核和偏置项
# if __name__ == '__main__':
# input = np.array([[[1,2,3],[4,5,6],[7,8,9]],
# [[10,11,12],[13,14,15],[16,17,18]],
# [[19,20,21],[22,23,24],[25,26,27]],
# [[28,29,30],[31,32,33],[34,35,36]]])
# AjConvPara=setajconv_para()
# kernel_new, bias_new = ajconv_proc(input, AjConvPara)
# print("\nkernel_new =\n", kernel_new,"\n")
# print("bias_new =", bias_new)
def setajfc_para():
AjFCPara = {
'weights': np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]), # 全连接权重
'bias': np.array([0.5, 0.6]), # 全连接偏置
'learning_rate': 0.01, # 学习率
'error': np.array([0.001, 0.002]) # 误差值
}
return AjFCPara
def ajfullconn_proc(AjFCPara):
error_value = AjFCPara['error']
learning_rate = AjFCPara['learning_rate']
error_term = error_value # 全连接层的误差项等于误差值
gradient_weights = np.outer(error_term, learning_rate) # 计算权重梯度
gradient_bias = error_term * learning_rate # 计算偏置梯度
# 更新全连接层参数(梯度下降算法)
AjFCPara['weights'] -= gradient_weights
AjFCPara['bias'] -= gradient_bias
return AjFCPara
# if __name__ == '__main__':
# AjFCPara = setajfc_para()# 调用SetAjFCPara()设置参数
# print("初始化的全连接层参数:", AjFCPara)
# newajfcpara = ajfullconn_proc(AjFCPara)# 调用AjFullconnProc()更新全连接层参数
# print("更新后的全连接层参数:", newajfcpara)
def parameter():
global DataPara
global ConvPara
global PoolPara
global NonLPara
global ClassifyPara
global LabelPara
global ErrorPara
DataPara = setload_data() # 调用setload_data()函数,获取加载数据集的参数
ConvPara = setconv_para() # 调用SetConvPara()函数,获取卷积层参数
PoolPara = setpool_para() # 调用设置池化层参数的函数,获取池化参数
NonLPara = setnonl_para() # 调用设置非线性参数的函数,获取非线性参数
ClassifyPara = setclassify_para() # 设置分类函数参数
LabelPara = setlabel_para() # 设置标签类别参数
ErrorPara = seterror_para() # 设置误差参数
def main():
# 创建一个tkinter根窗口并隐藏它
root = tk.Tk()
root.withdraw()
parameter()
global DataPara
train_images, test_images = load_data(DataPara) # 调用LoadData()函数,根据参数加载数据集
conv_images = convolutional_operation(train_images) # 存储卷积处理后的图片的列表
pool_images = pooling_treatment(conv_images) # 存储还回池化处理后的图片的列表
global FullConnPara
FullConnPara = setfullconn_para(pool_images[0]) # 调用设置全连接层参数的函数,获取全连接参数
fullconn_images = fullyconnected_operation(pool_images) # 存储全连接处理后的图片的列表
nonlinear_images = activation(fullconn_images) # 存储非线性处理后的图片的列表
classifier_images, prob_images = classify(nonlinear_images) # 存储分类处理后的图片的列表
labeled_samples = label_proc(nonlinear_images, prob_images, classifier_images) # 读取之前的分类,每一行是一个样本,每一列是一个特征,将分类标签放在最后一列
loss = loss_count(prob_images)
# 打印结果
print(loss)
if __name__ == '__main__':
main()