|
|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
×
和豆包一起熬了三个大存档,才写出来的
但是确实了解到了很多知识了
这里吧代码分享出来
和神经网络有关数学知识
太多了
这里我简单提一下
是高中会学的矩阵变换
in_向量*[w1]+b1=hidden_向量
hidden_向量(激活)*[w2]+b2=out_向量
w1,w2,b1,b2是网络矩阵,通过学习调整这些参数
in_向量是你的问题,out_向量是他的结果
刚才说到的激活是指
将hidden中每一项的分量如果<0,输出0,否则输出自己本身
使自己具有非线性能力(对于不同需要跳变的问题有不同计算方式)
那么为什么几乎所有问题都可以用这种方式解决呢?
那也不是,只是大部分问题了
因为神经网络它会自己去泛化这种问题的解题公式
但是你不知到它的过程
这就是
黑盒
内部数据是很难看懂的
总结到这了
使用文档在代码里
- import numpy as np
- """
- simple_nn 单隐藏层神经网络模块,仅支持监督训练
- ==============================
- 整体架构:输入层 → 隐藏层(LeakyReLU) → 输出层(线性,无激活)
- 设计思路:
- 神经网络内部计算只负责非线性变换,不强行把输出压缩到0~1。
- sigmoid 单独作为外部后处理函数,使用者按需调用,用来把原生输出映射至0~1。
- D1参数:LeakyReLU负数区间斜率,缓解原生ReLU神经元死亡问题。
- 对外可用函数:
- leaky_relu(x, d1=0.01)
- leaky_relu_deriv(x, d1=0.01)
- sigmoid(x) # 独立后处理函数,映射实数到0~1
- NeuralNet 类方法列表:
- forward(x):正向传播,返回前向快照对象ForwardResult
- fit(x, y_true):监督训练,输入+标准答案,自动反向传播更新权重
- custom_learn(x, score_func):自定义外部打分函数训练,不需要标准答案
- set_weights_manual(w1_list,b1_list,w2_list,b2_list):
- 用原生Python嵌套列表手动设置权重偏置,不经过训练
- ForwardResult 快照对象属性:
- x: 输入数据
- z1: 隐藏层激活前的值
- a1: 隐藏层LeakyReLU激活后的值
- z2: 输出层激活前的值
- a2: 网络原生输出(线性,无值域压缩,可正可负)
- calc_loss(y_true): 计算均方误差损失
- #-----------------------------#
- 参数:
- T=4.0、F=-4.0、mid=0.0 为 logit 域目标;
- 经过 sigmoid 压缩后对应概率约 0.98、0.02、0.5。
- T、F 是偏向两端的目标,mid 是中间不确定目标。
- 用于快捷匹配。
- bound_dict是默认事件字典序
- 事件字典序
- 用途:接收0~1的概率值,按预设的区间规则解释成value
- 约定:字典key是区间起点(闭区间),value是对应标签;书写顺序无关,查询时自动排序
- 注意:仅用于推理阶段解释概率,训练目标是logit的 T、F、mid
- """
- # ===== 激活函数(模块内可用,sigmoid单独独立出来,用户按需调用) =====
- def leaky_relu(x, d1=0.01):
- return np.where(x > 0, x, d1 * x)
- def leaky_relu_deriv(x, d1=0.01):
- return np.where(x > 0, 1.0, d1)
- # Sigmoid单独独立!网络内部forward不再自动调用它
- def sigmoid(x):
- return 1.0 / (1.0 + np.exp(-x))
- def logit(p):
- """
- 逆Sigmoid函数:0~1概率转回对应的logit值
- :param p: 概率,范围0~1
- :return: logit实数
- """
- q = np.clip(p, 1e-6, 1 - 1e-6)
- return np.log(q / (1 - q))
- #比对函数(事件字典序查找)
- def lookup_tag(p, bound_dict):
- "对事件字典序进行查找"
- if p < 0.0 or p > 1.0:
- raise ValueError("概率数值必须在 0.0 ~ 1.0 之间")
- start_points = sorted(bound_dict.keys())
- selected = start_points[0]
- for s in start_points:
- if s <= p:
- selected = s
- else:
- break
- return bound_dict[selected]
- #工具包
- def find_max_index(lst):
- """寻找最大值下标"""
- idx = lst.index(max(lst))
- return idx
-
- def find_max_key(d):
- """寻找最大值的键,d.type为dict"""
- best_key = max(d, key=d.get)
- return best_key
- # ===== 前向结果快照类 =====
- class ForwardResult:
- def __init__(self, x, z1, a1, z2, a2):
- self.x = x
- self.z1 = z1
- self.a1 = a1
- self.z2 = z2
- self.a2 = a2 # a2现在是【原生线性输出】,没有经过sigmoid,可以是任意实数
- self.out = a2.flatten().tolist()
- """out才是结果向量"""
- def calc_loss(self, y_true):
- """内置损失:均方误差,fit方法使用"""
- return np.mean((self.a2 - y_true) ** 2)
- # ===== 神经网络主类 =====
- class NeuralNet:
- def __init__(self,input_size,hidden_size,output_size,init=True, d1=0.01):
- self.in_size = input_size
- self.hidden_size = hidden_size
- self.out_size = output_size
- self.d1 = d1 # LeakyReLU的负区间斜率D1,用来防止神经元死亡
-
- if init:
- self.w1 = np.random.randn(input_size, hidden_size) * 0.1
- else:
- self.w1 = np.zeros((input_size, hidden_size))
- self.b1 = np.zeros((1, hidden_size))
-
- if init:
- self.w2 = np.random.randn(hidden_size, output_size) * 0.1
- else:
- self.w2 = np.zeros((hidden_size, output_size))
- self.b2 = np.zeros((1, output_size))
- self.learn_rate = 0.1#学习率
- # 方法1:正向传播
- def forward(self, x):
- x=np.array([x])
- z1 = np.dot(x, self.w1) + self.b1
- a1 = leaky_relu(z1, self.d1)
- z2 = np.dot(a1, self.w2) + self.b2
- a2 = z2 # 输出层:线性,**不套sigmoid**,原生输出
- return ForwardResult(x, z1, a1, z2, a2)
- # 方法2:传统训练:输入+真实标签,自动一步训练(前向+反向更新)
- def fit(self, x, y_true):
- y_true = np.array([y_true])
- res = self.forward(x)
- loss = res.calc_loss(y_true)
- # ===== 输出层现在是线性激活,导数=1!=====
- dZ2 = (res.a2 - y_true) * 1.0
- dW2 = np.dot(res.a1.T, dZ2)
- db2 = np.sum(dZ2, axis=0, keepdims=True)
- # 隐藏层梯度:LeakyReLU导数,使用z1
- dZ1 = np.dot(dZ2, self.w2.T) * leaky_relu_deriv(res.z1, self.d1)
- dW1 = np.dot(res.x.T, dZ1)
- db1 = np.sum(dZ1, axis=0, keepdims=True)
- self.w2 -= self.learn_rate * dW2
- self.b2 -= self.learn_rate * db2
- self.w1 -= self.learn_rate * dW1
- self.b1 -= self.learn_rate * db1
- return loss
- # 5.链式反向传播,计算各参数梯度
- dW2 = np.dot(res.a1.T, dZ2)
- db2 = np.sum(dZ2, axis=0, keepdims=True)
- dZ1 = np.dot(dZ2, self.w2.T) * leaky_relu_deriv(res.z1, self.d1)
- dW1 = np.dot(x.T, dZ1)
- db1 = np.sum(dZ1, axis=0, keepdims=True)
- # 6.更新权重
- self.w2 -= self.learn_rate * dW2
- self.b2 -= self.learn_rate * db2
- self.w1 -= self.learn_rate * dW1
- self.b1 -= self.learn_rate * db1
- return loss
-
- # 方法4:手动设置权重,输入为原生嵌套列表
- def set_weights_manual(self, w1_list, b1_list, w2_list, b2_list):
- """
- 手动填入权重、偏置,使用Python嵌套list,不是numpy数组
- w1_list: 嵌套列表,shape [input_size, hidden_size]
- b1_list: 嵌套列表,shape [1, hidden_size]
- w2_list: 嵌套列表,shape [hidden_size, output_size]
- b2_list: 嵌套列表,shape [1, output_size]
- """
- new_w1 = np.array(w1_list, dtype=np.float64)
- new_b1 = np.array(b1_list, dtype=np.float64)
- new_w2 = np.array(w2_list, dtype=np.float64)
- new_b2 = np.array(b2_list, dtype=np.float64)
- if new_w1.shape != (self.in_size, self.hidden_size):
- raise ValueError(f"w1_list 维度错误!需要 ({self.in_size},{self.hidden_size})")
- if new_b1.shape != (1, self.hidden_size):
- raise ValueError(f"b1_list 维度错误!需要 (1,{self.hidden_size})")
- if new_w2.shape != (self.hidden_size, self.out_size):
- raise ValueError(f"w2_list 维度错误!需要 ({self.hidden_size},{self.out_size})")
- if new_b2.shape != (1, self.out_size):
- raise ValueError(f"b2_list 维度错误!需要 (1,{self.out_size})")
-
- self.w1 = new_w1
- self.b1 = new_b1
- self.w2 = new_w2
- self.b2 = new_b2
- def train(self,callback,epoch=10):#批训练 epoch:次数
- """
- 函数callback无输入,输出两个列表in和out 自动生成训练数据
- last输出最后一次损失
- """
- for i in range(epoch):
- in_,out_=callback()
- last = self.fit(in_,out_)
- return last
-
- def explore(self,temp):
- """给自己产生神经扰动并附加于自身"""
- noise_w1 = np.random.normal(-temp,temp,size=self.w1.shape)
- self.w1 += noise_w1
- noise_w2 = np.random.normal(-temp,temp,size=self.w2.shape)
- self.w2 += noise_w2
-
- def copy(self):
- """返回自身数据到新的NeuralNet对象"""
- co = NeuralNet(self.input_size,self.hidden_size,self.output_size,dl = self.dl)
- co.learn_rate = self.learn_rate
- co.w1 = self.w1.copy()
- co.b1 = self.b1.copy()
- co.w2 = self.w2.copy()
- co.b2 = self.b2.copy()
- return co
- #默认参数
- T=sigmoid(4.0)#在神经训练中大约为sigmoid(0.98)近似表示是
- F=sigmoid(-4.0)#在神经训练中大约为sigmoid(0.02)近似表示否
- mid=sigmoid(0)#中间
- bound_dict = {
- 0.0: "NO",
- 0.1: "UNCERTAIN",
- 0.9: "YES"
- }
复制代码
还有测试程序
- import simple_nn
- ne = simple_nn.NeuralNet(2,10,1)
- ne.learn_rate = 0.02
- in_ = ([0,0],[0,1],[1,0],[1,1])
- out_ = (0, 1, 1, 0) # 与门
- for i in range(0,19999):
- if i % 1000== 0:
- print(f"训练了{i}次")
- for x_list, target in zip(in_, out_):
- if target==0:
- loss = ne.fit(x_list,[simple_nn.logit(0.05)])
- else:
- loss = ne.fit(x_list,[simple_nn.logit(0.95)])
- if i % 1000 == 0:
- print(f"轮{i}, loss={loss:.4f}")
- print("\n=====预测,输入两个0/1=====")
- v1 = float(input("输入第一个值:"))
- v2 = float(input("输入第二个值:"))
- res = ne.forward([v1,v2])
- print("神经网络输出out:", res.a2[0][0])
- p = simple_nn.sigmoid(res.out[0])
- print("映射概率sigmoid0~1:", p)
- if p>=0.9:
- print("答案是1吗?")
- if p<=0.1:
- print("答案是0吗?")
复制代码
大致功能,给它一大堆或门的真值表
最后看它记不记得住
可以把v1,v2换成浮点值发现它可以得出答案
说明程序成功运行
记得在pip下载numpy
|
|