Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
630 changes: 532 additions & 98 deletions chap1_warmup/numpy_ tutorial.ipynb

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions chap1_warmup/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[project]
name = "chap1-warmup"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.11"
dependencies = [
"ipykernel>=7.2.0",
"matplotlib>=3.10.8",
"numpy>=2.4.2",
]
1,044 changes: 1,044 additions & 0 deletions chap1_warmup/uv.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions chap4_ simple neural network/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
257 changes: 257 additions & 0 deletions chap4_ simple neural network/function_fitting_predictions.csv

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions chap4_ simple neural network/function_fitting_relu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import math
from pathlib import Path

import numpy as np


def target_function(x: np.ndarray) -> np.ndarray:
return np.sin(1.5 * x) + 0.3 * x**2


class ReLUNetwork:
def __init__(self, hidden_dim: int = 64, seed: int = 42) -> None:
rng = np.random.default_rng(seed)
self.W1 = rng.normal(0.0, math.sqrt(2.0), size=(1, hidden_dim))
self.b1 = np.zeros((1, hidden_dim))
self.W2 = rng.normal(0.0, math.sqrt(2.0 / hidden_dim), size=(hidden_dim, 1))
self.b2 = np.zeros((1, 1))

def forward(self, x: np.ndarray) -> np.ndarray:
self.x = x
self.z1 = x @ self.W1 + self.b1
self.h1 = np.maximum(self.z1, 0.0)
self.y_pred = self.h1 @ self.W2 + self.b2
return self.y_pred

def backward(self, y_true: np.ndarray) -> float:
n = y_true.shape[0]
diff = self.y_pred - y_true
loss = float(np.mean(diff**2))

grad_y = 2.0 * diff / n
grad_W2 = self.h1.T @ grad_y
grad_b2 = np.sum(grad_y, axis=0, keepdims=True)

grad_h1 = grad_y @ self.W2.T
grad_z1 = grad_h1 * (self.z1 > 0.0)
grad_W1 = self.x.T @ grad_z1
grad_b1 = np.sum(grad_z1, axis=0, keepdims=True)

self.grads = {
"W1": grad_W1,
"b1": grad_b1,
"W2": grad_W2,
"b2": grad_b2,
}
return loss

def step(self, lr: float) -> None:
self.W1 -= lr * self.grads["W1"]
self.b1 -= lr * self.grads["b1"]
self.W2 -= lr * self.grads["W2"]
self.b2 -= lr * self.grads["b2"]


def build_dataset(seed: int = 7):
rng = np.random.default_rng(seed)
x_train = rng.uniform(-3.0, 3.0, size=(512, 1))
y_train = target_function(x_train)

x_test = np.linspace(-3.0, 3.0, 256, dtype=np.float64).reshape(-1, 1)
y_test = target_function(x_test)
return x_train, y_train, x_test, y_test


def save_results(x_test: np.ndarray, y_test: np.ndarray, y_pred: np.ndarray) -> None:
output = Path("function_fitting_predictions.csv")
data = np.concatenate([x_test, y_test, y_pred], axis=1)
np.savetxt(
output,
data,
delimiter=",",
header="x,true_y,pred_y",
comments="",
)


def main() -> None:
x_train, y_train, x_test, y_test = build_dataset()
model = ReLUNetwork(hidden_dim=64, seed=42)

epochs = 5000
learning_rate = 1e-2

for epoch in range(1, epochs + 1):
model.forward(x_train)
train_loss = model.backward(y_train)
model.step(learning_rate)

if epoch % 500 == 0 or epoch == 1:
test_pred = model.forward(x_test)
test_loss = float(np.mean((test_pred - y_test) ** 2))
print(
f"epoch={epoch:4d} "
f"train_loss={train_loss:.6f} "
f"test_loss={test_loss:.6f}"
)

final_pred = model.forward(x_test)
final_test_loss = float(np.mean((final_pred - y_test) ** 2))
print(f"final_test_loss={final_test_loss:.6f}")
save_results(x_test, y_test, final_pred)
print("saved=function_fitting_predictions.csv")


if __name__ == "__main__":
main()
45 changes: 45 additions & 0 deletions chap4_ simple neural network/function_fitting_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 函数拟合报告

## 1. 函数定义

本实验选择的目标函数为:

\[
f(x)=\sin(1.5x)+0.3x^2
\]

这个函数同时包含非线性振荡项和二次项,能够比较直观地观察两层 ReLU 网络的拟合能力。

## 2. 数据采样

- 训练集:在区间 `[-3, 3]` 上随机采样 512 个点。
- 测试集:在区间 `[-3, 3]` 上均匀采样 256 个点。
- 标签:由目标函数直接计算得到。

## 3. 模型描述

使用一个两层 ReLU 神经网络进行拟合:

- 输入维度:1
- 隐藏层:64 个神经元
- 激活函数:ReLU
- 输出维度:1
- 损失函数:均方误差(MSE)
- 优化方式:手写反向传播 + 梯度下降

对应实现见 [function_fitting_relu.py](/e:/大三下/dl/exercise/chap4_%20simple%20neural%20network/function_fitting_relu.py)。

## 4. 拟合效果

脚本会输出训练过程中的训练集与测试集损失,并生成 `function_fitting_predictions.csv`,其中包含:

- `x`
- `true_y`
- `pred_y`

可以据此自行绘制真实函数曲线与预测曲线,对拟合效果进行可视化验证。

## 5. 说明

- 该实现仅依赖 `numpy`,不依赖 TensorFlow/PyTorch,便于直接运行。
- 如果后续需要,也可以很容易改写成 TensorFlow 版本,用于与本章前面的全连接网络练习保持统一。
6 changes: 6 additions & 0 deletions chap4_ simple neural network/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main():
print("Hello from chap4-simple-neural-network!")


if __name__ == "__main__":
main()
11 changes: 11 additions & 0 deletions chap4_ simple neural network/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "chap4-simple-neural-network"
version = "0.1.0"
description = "Dependencies for chap4 simple neural network exercises"
readme = "README.md"
requires-python = ">=3.10,<3.13"
dependencies = [
"ipykernel>=6.29,<7",
"numpy>=1.26,<3",
"tensorflow>=2.16,<3",
]
14 changes: 14 additions & 0 deletions chap4_ simple neural network/tf2.0-exercise.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
" '''实现softmax函数,只要求对最后一维归一化,\n",
" 不允许用tf自带的softmax函数'''\n",
" ##########\n",
" x = tf.convert_to_tensor(x, dtype=tf.float32)\n",
" x = x - tf.reduce_max(x, axis=-1, keepdims=True)\n",
" exp_x = tf.exp(x)\n",
" prob_x = exp_x / tf.reduce_sum(exp_x, axis=-1, keepdims=True)\n",
" return prob_x\n",
"\n",
"test_data = np.random.normal(size=[10, 5])\n",
Expand All @@ -58,6 +62,8 @@
" ##########\n",
" '''实现sigmoid函数, 不允许用tf自带的sigmoid函数'''\n",
" ##########\n",
" x = tf.convert_to_tensor(x, dtype=tf.float32)\n",
" prob_x = 1.0 / (1.0 + tf.exp(-x))\n",
" return prob_x\n",
"\n",
"test_data = np.random.normal(size=[10, 5])\n",
Expand All @@ -81,6 +87,10 @@
" ##########\n",
" '''实现 softmax 交叉熵loss函数, 不允许用tf自带的softmax_cross_entropy函数'''\n",
" ##########\n",
" x = tf.convert_to_tensor(x, dtype=tf.float32)\n",
" label = tf.convert_to_tensor(label, dtype=tf.float32)\n",
" eps = tf.constant(1e-12, dtype=x.dtype)\n",
" loss = -tf.reduce_mean(tf.reduce_sum(label * tf.math.log(x + eps), axis=-1))\n",
" return loss\n",
"\n",
"test_data = np.random.normal(size=[10, 5])\n",
Expand Down Expand Up @@ -117,6 +127,10 @@
" ##########\n",
" '''实现 softmax 交叉熵loss函数, 不允许用tf自带的softmax_cross_entropy函数'''\n",
" ##########\n",
" x = tf.convert_to_tensor(x, dtype=tf.float32)\n",
" label = tf.convert_to_tensor(label, dtype=tf.float32)\n",
" eps = tf.constant(1e-12, dtype=x.dtype)\n",
" loss = -tf.reduce_mean(label * tf.math.log(x + eps) + (1.0 - label) * tf.math.log(1.0 - x + eps))\n",
" return loss\n",
"\n",
"test_data = np.random.normal(size=[10])\n",
Expand Down
Loading