import numpy as np
import lqmtest_x3_5_pool
import lqmtest_x3_2_load_data
import lqmtest_x3_4_conv_proc
import lqmtest_x3_6_fullconn
import lqmtest_x3_7_nonlinear
import lqmtest_x3_8_classify
import lqmtest_x3_9_label
import lqmtest_x3_10_error
class ModelObj: # 网络对象
    def __init__(self, ObjID, ObjType, ObjLable, ParaString, ObjX, ObjY):
        self.ObjID = ObjID  # 图元号
        self.ObjType = ObjType  # 图元类别
        self.ObjLable = ObjLable  # 对象标签
        self.ParaString = ParaString  # 参数字符串
        self.ObjX = ObjX  # 对象位置x坐标
        self.ObjY = ObjY  # 对象位置y坐标

class AjConv_Class(ModelObj):  # 卷积调整对象
    def __init__(self, ObjID, ObjType, ObjLable, ParaString, ObjX, ObjY):
        super().__init__(ObjID, ObjType, ObjLable, ParaString, ObjX, ObjY)
        self.AjConvProc = self.ajconv_proc  # 基本操作函数
        self.SetAjConvPara = self.setajconv_para  # 参数设置函数
    def ajconv_proc(self, images, AjConvPara):
        kernel_grad_list = []
        bias_grad = 0  # 初始化偏置项梯度为零
        for c in images:
            # 计算卷积核和偏置项的梯度
            # 初始化卷积核梯度为零矩阵
            kernel_grad = np.zeros_like(AjConvPara['kernel_info'])
            for i in range(AjConvPara['loss'].shape[0]):
                for j in range(AjConvPara['loss'].shape[1]):
                    # 将输入数据数组中对应的子矩阵旋转180度，
                    # 与误差值相乘，累加到卷积核梯度上
                    kernel_grad+=np.rot90(c[i:i+kernel_grad.shape[0],
                                        j:j + kernel_grad.shape[0]],
                                        2) * AjConvPara['loss'][i, j]
                    # 将误差值累加到偏置项梯度上
                    bias_grad += AjConvPara['loss'][i, j]
            kernel_grad_list.append(kernel_grad)
        # 使用stack函数沿着第0个轴把一百个a数组堆叠起来
        result = np.stack(kernel_grad_list, axis=0)
        # 沿着第0个维度求和
        kernel_grad = np.sum(result, axis=0) / len(images)
        # 更新卷积核和偏置项参数
        kernel = AjConvPara['kernel_info']-AjConvPara['learning_rate'
                    ] * kernel_grad  # 卷积核参数减去学习率乘以卷积核梯度
        return kernel  # 返回更新后的卷积核
    def setajconv_para(self, loss,  ConvPara):
        kernel = ConvPara['kernel']  # 卷积核信息
        learning_rate = float(input("请输入卷积调整的学习率: "))  # 0.01  学习率
        loss = np.array([[loss]])
        AjConvPara = {'kernel_info': kernel, 'learning_rate':
                                        learning_rate, 'loss': loss}
        return AjConvPara
if __name__ == '__main__':
    DataSet = lqmtest_x3_2_load_data.Data_Class("DataSet1", 1, "数据集1", [], 120, 330)
    # setload_data()函数，获取加载数据集的参数
    DataPara = DataSet.SetDataPara()
    train_images, test_images = DataSet.LoadData(DataPara)

    Conv = lqmtest_x3_4_conv_proc.Conv_Class("Conv1", 2, "卷积1", [], 250, 330)
    ConvPara = Conv.SetConvPara()
    for i in range(len(train_images) // 32):
        images = train_images[i * 32:(i + 1) * 32]
        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.ConvProc(image, ConvPara)
                conv_images.append(output)  # 将卷积结果存储到列表
            elif dim == 3:  # 若为三维矩阵，则保持不变直接卷积处理
                output = Conv.ConvProc(image, ConvPara)
                conv_images.append(output)
        # 将卷积处理后的图片列表转换为数组形式，方便后续处理
        conv_images = np.array(conv_images)
    
    Pool = lqmtest_x3_5_pool.Pool_Class("Pool1", 3, "最大池化1", [], 380, 330)
    PoolPara = Pool.SetPollPara()
    pool_images = []  # 存储池化处理后的图片的列表
    for image in conv_images:  # 获取卷积后的图片数据
        output = Pool.MaxPoolProc(image, PoolPara)
        pool_images.append(output)  # 将池化结果存储到列表
    # 将池化处理后的图片列表转换为数组形式，方便后续处理
    pool_images = np.array(pool_images)
    
    _, _, poolH, poolW = pool_images.shape
    FullConn = lqmtest_x3_6_fullconn.FullConn_Class("FullConn1", 4, "全连接1", [], 510, 330)
    FullConnPara = FullConn.SetFullConnPara(poolH, poolW)
    
    fullconn_images = []      # 存储全连接处理后的图片的列表
    for image in pool_images: # 获取池化后的图片数据
        output = FullConn.FullConnProc(image, FullConnPara)
        fullconn_images.append(output)  # 将全连接处理后的结果存储到列表
    # 将全连接处理后的图片列表转换为数组形式，方便后续处理
    fullconn_images = np.array(fullconn_images)
    
    Nonline = lqmtest_x3_7_nonlinear.Nonline_Class("Nonline1", 5, "非线性函数1", [], 640, 330)
    NonLPara = Nonline.SetNonLPara()
    # 存储非线性处理后的图片的列表
    nonlinear_images = []
    for image in fullconn_images:  # 获取全连接处理后的图片数据
        output = Nonline.NonlinearProc(image, NonLPara)
        # 将非线性处理后的结果存储到列表
        nonlinear_images.append(output)
    # 将非线性处理后的图片列表转换为数组形式，方便后续处理
    nonlinear_images = np.array(nonlinear_images)
    
    Classifier=lqmtest_x3_8_classify.Classifier_Class("Classifier1", 6, "分类1", [], 780, 330)
    ClassifyPara = Classifier.SetClassifyPara()
    classifier_images = []  # 存储分类处理后的图片的列表
    prob_images = []  # 存储分类处理后的概率向量
    for image in nonlinear_images:  # 获取非线性处理后的图片数据
        # 调用softmax函数，得到概率分布向量
        prob = Classifier.softmax(image)
        prob_images.append(prob)  # 将概率向量结果存储到列表
        output = Classifier.ClassifierProc(image, ClassifyPara)
        classifier_images.append(output)  # 将分类结果存储到列表
    # 将分类的结果列表转换为数组形式，方便后续处理
    classifier_images = np.array(classifier_images)
    
    LabelPara = lqmtest_x3_9_label.Label()
    label_dict = LabelPara.setlabel_para()
    right_label = LabelPara.label_array(i)
    labeled_samples = LabelPara.label_proc(images,
                                        right_label, label_dict)
    
    Error = lqmtest_x3_10_error.Error_Class("Error1", 7, "误差计算1", [], 710, 124)
    ErrorPara = Error.SetErrorPara()
    # 输入值是一个概率矩阵，真实标签值是一个类别列表
    prob_images = np.squeeze(prob_images)
    loss = Error.ErrorProc(prob_images, right_label, ErrorPara)
    
    AjConv = AjConv_Class("AjConv1", 8, "卷积调整1", [], 250, 70)
    AjConvPara = AjConv.SetAjConvPara(loss, ConvPara)
    ConvPara['kernel'] = AjConv.AjConvProc(images, AjConvPara)
