This commit is contained in:
parent
6d89ffe182
commit
70431cad4f
Binary file not shown.
211
神经网络/main.py
211
神经网络/main.py
|
|
@ -34,7 +34,6 @@ class NeuralNetwork:
|
||||||
:param epsilon: 极小值,默认为1e-9
|
:param epsilon: 极小值,默认为1e-9
|
||||||
"""
|
"""
|
||||||
print("正在初始化神经网络...", end="")
|
print("正在初始化神经网络...", end="")
|
||||||
|
|
||||||
if not (
|
if not (
|
||||||
len(structure) >= 3
|
len(structure) >= 3
|
||||||
and all(x >= 1 if isinstance(x, int) else False for x in structure)
|
and all(x >= 1 if isinstance(x, int) else False for x in structure)
|
||||||
|
|
@ -55,66 +54,18 @@ class NeuralNetwork:
|
||||||
# 神经网络层数(定义,第0层为输入层,第l层为隐含层(l=1,2,...,L-1),第L层为输出层(L为神经网络层数),深度为L+1)
|
# 神经网络层数(定义,第0层为输入层,第l层为隐含层(l=1,2,...,L-1),第L层为输出层(L为神经网络层数),深度为L+1)
|
||||||
self.layer_counts = len(structure) - 1
|
self.layer_counts = len(structure) - 1
|
||||||
|
|
||||||
# 初始化是否训练
|
|
||||||
self.training = True
|
|
||||||
|
|
||||||
numpy.random.seed(seed) # 设置随机种子
|
|
||||||
|
|
||||||
self.parameters = {0: {}}
|
|
||||||
# 初始化神经网络参数
|
# 初始化神经网络参数
|
||||||
for layer_index in range(1, self.layer_counts + 1):
|
self.parameters = {}
|
||||||
self.parameters[layer_index] = {
|
|
||||||
"activate": (
|
|
||||||
activate := (
|
|
||||||
self.output_activate
|
|
||||||
if layer_index == self.layer_counts
|
|
||||||
else self.hidden_activate
|
|
||||||
)
|
|
||||||
), # 激活函数
|
|
||||||
"weight": self._init_weight(
|
|
||||||
activate=activate,
|
|
||||||
previous_layer_neuron_counts=self.structure[layer_index - 1],
|
|
||||||
current_layer_neuron_counts=(
|
|
||||||
current_layer_neuron_counts := self.structure[layer_index]
|
|
||||||
),
|
|
||||||
), # 权重,维度为[当前层神经元数,上一层神经元数],适配加权输入=权重*输入+平移
|
|
||||||
"bias": numpy.zeros((current_layer_neuron_counts, 1)), # 平移
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# 初始化模式(包括训练模式和推理模式)
|
||||||
|
self.training = None
|
||||||
|
# 初始化随机种子
|
||||||
|
self.seed = seed
|
||||||
|
# 初始化极小值
|
||||||
self.epsilon = epsilon
|
self.epsilon = epsilon
|
||||||
|
|
||||||
print("已完成")
|
print("已完成")
|
||||||
|
|
||||||
def _init_weight(
|
|
||||||
self,
|
|
||||||
activate: Literal["relu", "linear", "softmax"],
|
|
||||||
previous_layer_neuron_counts: int,
|
|
||||||
current_layer_neuron_counts: int,
|
|
||||||
) -> numpy.floating:
|
|
||||||
"""
|
|
||||||
初始化权重
|
|
||||||
:param activate: 激活函数
|
|
||||||
:param previous_layer_neuron_counts: 上一层神经元数
|
|
||||||
:param current_layer_neuron_counts: 当前层神经元数
|
|
||||||
:return: 初始化后的权重,维度为[当前层神经元数,上一层神经元数]
|
|
||||||
"""
|
|
||||||
weight = numpy.random.randn(
|
|
||||||
current_layer_neuron_counts, previous_layer_neuron_counts
|
|
||||||
)
|
|
||||||
match activate:
|
|
||||||
case "relu":
|
|
||||||
return weight * numpy.sqrt(
|
|
||||||
2 / previous_layer_neuron_counts
|
|
||||||
) # 使用He初始化权重方法
|
|
||||||
case "linear":
|
|
||||||
return weight * numpy.sqrt(
|
|
||||||
2 / previous_layer_neuron_counts
|
|
||||||
) # 使用He初始化权重方法
|
|
||||||
case "softmax":
|
|
||||||
return weight * numpy.sqrt(
|
|
||||||
2 / (previous_layer_neuron_counts + current_layer_neuron_counts)
|
|
||||||
) # 使用Xavier初始化权重方法
|
|
||||||
|
|
||||||
def train(
|
def train(
|
||||||
self,
|
self,
|
||||||
X: numpy.ndarray,
|
X: numpy.ndarray,
|
||||||
|
|
@ -145,7 +96,10 @@ class NeuralNetwork:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"输入和真实输出应为数组,其中输入维度应为[输入神经元数, 样本数],真实输出维度应为[输出神经元数, 样本数],样本数应需相同"
|
"输入和真实输出应为数组,其中输入维度应为[输入神经元数, 样本数],真实输出维度应为[输出神经元数, 样本数],样本数应需相同"
|
||||||
)
|
)
|
||||||
|
# 默认为训练模式
|
||||||
self.training = True
|
self.training = True
|
||||||
|
# 初始化神经网络参数
|
||||||
|
self._init_parameters()
|
||||||
# 归一化输入
|
# 归一化输入
|
||||||
self.parameters[0].update({"activation": self._normalize(input=X)})
|
self.parameters[0].update({"activation": self._normalize(input=X)})
|
||||||
|
|
||||||
|
|
@ -174,12 +128,80 @@ class NeuralNetwork:
|
||||||
|
|
||||||
epoch += 1
|
epoch += 1
|
||||||
|
|
||||||
try:
|
# 保存神经网络参数
|
||||||
with open("neural_network.pkl", "wb") as file:
|
self._save_parameters()
|
||||||
pickle.dump(self, file, protocol=pickle.HIGHEST_PROTOCOL)
|
|
||||||
print(f"模型保存成功")
|
def _init_parameters(self) -> None:
|
||||||
except Exception as exception:
|
"""
|
||||||
raise RuntimeError(f"模型保存失败:{str(exception)}") from exception
|
初始化神经网络参数
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
self.parameters = {0: {}}
|
||||||
|
# 初始化神经网络参数
|
||||||
|
for layer_index in range(1, self.layer_counts + 1):
|
||||||
|
self.parameters[layer_index] = {
|
||||||
|
"activate": (
|
||||||
|
activate := (
|
||||||
|
self.output_activate
|
||||||
|
if layer_index == self.layer_counts
|
||||||
|
else self.hidden_activate
|
||||||
|
)
|
||||||
|
), # 激活函数
|
||||||
|
"weight": self._init_weight(
|
||||||
|
activate=activate,
|
||||||
|
previous_layer_neuron_counts=self.structure[layer_index - 1],
|
||||||
|
current_layer_neuron_counts=(
|
||||||
|
current_layer_neuron_counts := self.structure[layer_index]
|
||||||
|
),
|
||||||
|
), # 权重,维度为[当前层神经元数,上一层神经元数],适配加权输入=权重*输入+平移
|
||||||
|
"bias": self._init_bias(
|
||||||
|
current_layer_neuron_counts=current_layer_neuron_counts
|
||||||
|
), # 平移
|
||||||
|
}
|
||||||
|
|
||||||
|
def _init_weight(
|
||||||
|
self,
|
||||||
|
activate: str,
|
||||||
|
previous_layer_neuron_counts: int,
|
||||||
|
current_layer_neuron_counts: int,
|
||||||
|
) -> numpy.ndarray: # pyright: ignore[reportReturnType]
|
||||||
|
"""
|
||||||
|
初始化权重
|
||||||
|
:param activate: 激活函数
|
||||||
|
:param previous_layer_neuron_counts: 上一层神经元数
|
||||||
|
:param current_layer_neuron_counts: 当前层神经元数
|
||||||
|
:return: 初始化后的权重,维度为[当前层神经元数,上一层神经元数]
|
||||||
|
"""
|
||||||
|
# 设置随机种子
|
||||||
|
numpy.random.seed(self.seed)
|
||||||
|
# 基于正态分布生成权重
|
||||||
|
weight = numpy.random.randn(
|
||||||
|
current_layer_neuron_counts, previous_layer_neuron_counts
|
||||||
|
)
|
||||||
|
match activate:
|
||||||
|
case "relu":
|
||||||
|
return weight * numpy.sqrt(
|
||||||
|
2 / previous_layer_neuron_counts
|
||||||
|
) # 使用He初始化权重方法
|
||||||
|
case "linear":
|
||||||
|
return weight * numpy.sqrt(
|
||||||
|
2 / previous_layer_neuron_counts
|
||||||
|
) # 使用He初始化权重方法
|
||||||
|
case "softmax":
|
||||||
|
return weight * numpy.sqrt(
|
||||||
|
2 / (previous_layer_neuron_counts + current_layer_neuron_counts)
|
||||||
|
) # 使用Xavier初始化权重方法
|
||||||
|
|
||||||
|
def _init_bias(
|
||||||
|
self,
|
||||||
|
current_layer_neuron_counts: int,
|
||||||
|
) -> numpy.ndarray:
|
||||||
|
"""
|
||||||
|
初始化平移
|
||||||
|
:param current_layer_neuron_counts: 当前层神经元数
|
||||||
|
:return: 初始化后的平移,维度为[当前层神经元数, 1]
|
||||||
|
"""
|
||||||
|
return numpy.zeros((current_layer_neuron_counts, 1))
|
||||||
|
|
||||||
def _normalize(
|
def _normalize(
|
||||||
self,
|
self,
|
||||||
|
|
@ -206,7 +228,7 @@ class NeuralNetwork:
|
||||||
def _forward_propagate(self) -> None:
|
def _forward_propagate(self) -> None:
|
||||||
"""
|
"""
|
||||||
前向传播
|
前向传播
|
||||||
:return: 输出层的输出预测,维度为[输出神经元数, 样本数]
|
:return: 输出层的预测输出,维度为[输出神经元数, 样本数]
|
||||||
"""
|
"""
|
||||||
for layer_index in range(1, self.layer_counts + 1):
|
for layer_index in range(1, self.layer_counts + 1):
|
||||||
self.parameters[layer_index].update(
|
self.parameters[layer_index].update(
|
||||||
|
|
@ -229,9 +251,9 @@ class NeuralNetwork:
|
||||||
|
|
||||||
def _activate(
|
def _activate(
|
||||||
self,
|
self,
|
||||||
activate: Literal["relu", "linear", "softmax"],
|
activate: str,
|
||||||
input: numpy.ndarray,
|
input: numpy.ndarray,
|
||||||
) -> numpy.ndarray:
|
) -> numpy.ndarray: # pyright: ignore[reportReturnType]
|
||||||
"""
|
"""
|
||||||
激活
|
激活
|
||||||
:param activate: 激活函数
|
:param activate: 激活函数
|
||||||
|
|
@ -385,23 +407,66 @@ class NeuralNetwork:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def _reason(self, input: numpy.ndarray) -> numpy.ndarray:
|
def _save_parameters(self) -> None:
|
||||||
|
"""
|
||||||
|
保存神经网络参数
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
with open("parameters.pkl", "wb") as file:
|
||||||
|
pickle.dump(
|
||||||
|
obj=self.parameters,
|
||||||
|
file=file,
|
||||||
|
protocol=pickle.HIGHEST_PROTOCOL,
|
||||||
|
)
|
||||||
|
|
||||||
|
def reason(self, X: numpy.ndarray) -> numpy.ndarray:
|
||||||
"""
|
"""
|
||||||
推理
|
推理
|
||||||
:param input: 输入,维度为[输入神经元数, 样本数]
|
:param X: 输入,维度为[输入神经元数, 样本数]
|
||||||
:return: 输出,维度为[输出神经元数, 样本数]
|
:return: 预测输出,维度为[输出神经元数, 样本数]
|
||||||
"""
|
"""
|
||||||
|
print(f"基于已训练神经网络进行推理...")
|
||||||
|
if not (
|
||||||
|
X.shape[0] == self.structure[0] if isinstance(X, numpy.ndarray) else False
|
||||||
|
):
|
||||||
|
raise RuntimeError("输入应为数组,输入维度应为[输入神经元数, 样本数]")
|
||||||
|
# 默认为推理模式
|
||||||
self.training = False
|
self.training = False
|
||||||
|
# 加载神经网络参数
|
||||||
|
self._load_parameters()
|
||||||
# 归一化输入
|
# 归一化输入
|
||||||
self.parameters[0].update({"activation": self._normalize(input=input)})
|
self.parameters[0].update({"activation": self._normalize(input=X)})
|
||||||
|
|
||||||
return self._forward_propagate()
|
# 前向传播
|
||||||
|
self._forward_propagate()
|
||||||
|
|
||||||
|
return self.parameters[self.layer_counts]["activation"]
|
||||||
|
|
||||||
|
def _load_parameters(self) -> None:
|
||||||
|
"""
|
||||||
|
加载神经网络参数
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
with open("parameters.pkl", "rb") as file:
|
||||||
|
self.parameters = pickle.load(file=file)
|
||||||
|
# 校验神经网络参数
|
||||||
|
for layer_index in range(1, self.layer_counts + 1):
|
||||||
|
if not (
|
||||||
|
self.parameters[layer_index]["weight"].shape
|
||||||
|
== (self.structure[layer_index], self.structure[layer_index - 1])
|
||||||
|
and self.parameters[layer_index]["bias"].shape
|
||||||
|
== (self.structure[layer_index], 1)
|
||||||
|
if isinstance(self.parameters[layer_index]["weight"], numpy.ndarray)
|
||||||
|
and isinstance(self.parameters[layer_index]["bias"], numpy.ndarray)
|
||||||
|
else False
|
||||||
|
):
|
||||||
|
raise RuntimeError("神经网络参数中权重和偏置的维度与神经网络结构不匹配")
|
||||||
|
|
||||||
|
|
||||||
# 测试代码
|
# 测试代码
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
X = numpy.random.randn(2, 100)
|
X = numpy.random.randn(2, 5000)
|
||||||
# 真实函数:y = 2*x1 + 3*x2 + 1
|
# 真实函数:y = 2*x1 + 3*x2 + 1
|
||||||
y_true = 2 * X[0:1, :] ** 2 + 3 * X[1:2, :] + 1
|
y_true = 2 * X[0:1, :] ** 2 + 3 * X[1:2, :] + 1
|
||||||
|
|
||||||
|
|
@ -412,5 +477,9 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# 训练
|
# 训练
|
||||||
neural_network.train(
|
neural_network.train(
|
||||||
X=X, y_true=y_true, target_loss=0.1, epochs=1_000, learning_rate=0.05
|
X=X, y_true=y_true, target_loss=0.01, epochs=1_000, learning_rate=0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(f"推理结果:{y_true[:, 0:5]}")
|
||||||
|
|
||||||
|
print(f"推理结果:{neural_network.reason(X=X)[:, 0:5]}")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue