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
9 changes: 3 additions & 6 deletions src/chap02_linear_regression/exercise-linear_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,8 @@ def multinomial_basis(x, feature_num=10):
feature_num: 多项式的最高次数
返回 shape (N, feature_num)"""
x = np.expand_dims(x, axis=1) # shape(N, 1)
# 生成各次幂特征:x^1, x^2, ..., x^feature_num,将其拼接
ret = [x**i for i in range(1, feature_num + 1)]
# 将存储不同次幂特征的数组在第二个维度(列方向)上进行拼接
# 例如,若每个特征数组形状为 (N, 1),拼接后形状变为 (N, feature_num)
ret = np.concatenate(ret, axis=1)
# 优化点:利用NumPy广播直接生成所有次幂特征,替代列表推导+拼接
ret = x ** np.arange(1, feature_num + 1)
return ret


Expand Down Expand Up @@ -329,4 +326,4 @@ def plot_results(x_train, y_train, x_test, y_test, y_test_pred):
print("预测值与真实值的标准差:{:.1f}".format(std))

# 使用封装的绘图函数
plot_results(x_train, y_train, x_test, y_test, y_test_pred)
plot_results(x_train, y_train, x_test, y_test, y_test_pred)
99 changes: 43 additions & 56 deletions src/chap02_linear_regression/linear_regression-tf2.0.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import numpy as np
# 导入Matplotlib的pyplot模块 - 用于数据可视化和绘图
import matplotlib.pyplot as plt
# 导入Matplotlib的pyplot模块 - 用于数据可视化和绘图
import tensorflow as tf # 导入TensorFlow深度学习框架,并使用别名'tf'简化调用
# 导入TensorFlow深度学习框架,并使用别名'tf'简化调用
import tensorflow as tf
# 从Keras导入常用模块
from tensorflow.keras import optimizers, layers, Model

Expand All @@ -17,20 +17,17 @@ def identity_basis(x):
返回形状为 (N, 1) 的数组,其中 N 是输入样本数"""
return np.expand_dims(x, axis=1)

# 生成多项式基函数
# 生成多项式基函数
def multinomial_basis(x, feature_num=10):
"""多项式基函数:将输入x映射为多项式特征
feature_num: 多项式的最高次数
返回形状为 (N, feature_num) 的数组"""
x = np.expand_dims(x, axis=1) # shape(N, 1)
# 初始化特征列表
feat = [x]
# 生成从 x^2 到 x^feature_num 的多项式特征
for i in range(2, feature_num + 1):
feat.append(x**i)
# 优化点1:用列表推导式精简多项式特征生成(替代原for循环)
feat = [x**i for i in range(1, feature_num + 1)]
# 将所有特征沿着第二维(axis=1)拼接起来
ret = np.concatenate(feat, axis=1)
return ret # 返回一个二维数组,其中每一行是输入样本的多项式特征向量,列数为 feature_num
return ret # 返回一个二维数组,其中每一行是输入样本的多项式特征向量,列数为 feature_num


def gaussian_basis(x, feature_num=10):
Expand All @@ -45,10 +42,10 @@ def gaussian_basis(x, feature_num=10):
width = 1.0 * (centers[1] - centers[0])
# 使用np.expand_dims在x的第1维度(axis=1)上增加一个维度以便广播计算
x = np.expand_dims(x, axis=1)
# 将x沿着第1维度(axis=1)复制feature_num次并连接使其与中心点数量匹配
x = np.concatenate([x] * feature_num, axis=1) # 将 x 沿着第 1 维度复制 feature_num 次
out = (x - centers) / width # 计算每个样本点到每个中心点的标准化距离,将输入值减去对应的中心点,然后除以宽度进行标准化
ret = np.exp(-0.5 * out ** 2) # 对标准化距离应用高斯函数,-0.5 * out**2 对应高斯分布的概率密度函数形式
# 可选优化:用np.tile替代concatenate,更高效(保留原逻辑也可)
x = np.tile(x, (1, feature_num)) # 等价于原np.concatenate逻辑
out = (x - centers) / width # 计算每个样本点到每个中心点的标准化距离
ret = np.exp(-0.5 * out ** 2) # 对标准化距离应用高斯函数
return ret


Expand All @@ -60,22 +57,21 @@ def load_data(filename, basis_func=gaussian_basis):
with open(filename, "r") as f:
for line in f:
# 读取每一行数据,并将其转换为浮点数列表
# 改进: 转换为list
xys.append(list(map(float, line.strip().split()))) # 读取每行数据将数据分离为特征和标签
xs, ys = zip(*xys) # 解压为特征和标签
xs, ys = np.asarray(xs), np.asarray(ys) # 转换为numpy数组
o_x, o_y = xs, ys # 保存原始数据
phi0 = np.expand_dims(np.ones_like(xs), axis=1) # 添加偏置项(全1列)
phi1 = basis_func(xs) # 应用基函数变换
xs = np.concatenate([phi0, phi1], axis=1)
xys.append(list(map(float, line.strip().split()))) # 读取每行数据将数据分离为特征和标签
xs, ys = zip(*xys) # 解压为特征和标签
xs, ys = np.asarray(xs), np.asarray(ys) # 转换为numpy数组
o_x, o_y = xs, ys # 保存原始数据
phi0 = np.expand_dims(np.ones_like(xs), axis=1) # 添加偏置项(全1列)
phi1 = basis_func(xs) # 应用基函数变换
xs = np.concatenate([phi0, phi1], axis=1)
# 拼接偏置和变换后的特征
return (np.float32(xs), np.float32(ys)), (o_x, o_y)# 返回处理好的训练数据和原始数据
return (np.float32(xs), np.float32(ys)), (o_x, o_y) # 返回处理好的训练数据和原始数据


#定义模型
# 定义模型
class LinearModel(Model):
"""线性回归模型,实现 y = w·x + b"""

def __init__(self, ndim):
"""
初始化线性模型
Expand All @@ -85,32 +81,29 @@ def __init__(self, ndim):
"""
# 调用父类(Model)的构造函数
super(LinearModel, self).__init__()

# 定义模型参数:权重矩阵 w
# 形状为 [ndim, 1],表示从 ndim 维输入到 1 维输出的线性变换
# 初始值从均匀分布 [-0.1, 0.1) 中随机生成
# trainable=True 表示该变量需要在训练过程中被优化
# 创建一个TensorFlow变量作为模型权重
self.w = tf.Variable(
shape=[ndim, 1], # 权重矩阵形状:ndim×1
shape=[ndim, 1], # 权重矩阵形状:ndim×1
initial_value=tf.random.uniform(
# [ndim, 1] 表示这是一个二维矩阵,有 ndim 行和 1 列
[ndim, 1], minval=-0.1, maxval=0.1, dtype=tf.float32
),
trainable=True,
name="weight"# 参数名称(用于TensorBoard等可视化工具)
name="weight" # 参数名称(用于TensorBoard等可视化工具)
)


# 注意:代码中缺少偏置项 b,完整的线性模型通常需要包含偏置
# 定义偏置参数b,形状为 [1]

self.b = tf.Variable(# 定义偏置参数b,它是一个TensorFlow的变量(Variable)
self.b = tf.Variable( # 定义偏置参数b,它是一个TensorFlow的变量(Variable)
initial_value=tf.zeros([1], dtype=tf.float32),
trainable=True,
name="bias"
)

@tf.function
def call(self, x):
"""模型前向传播
Expand All @@ -125,25 +118,25 @@ def call(self, x):
return y


(xs, ys), (o_x, o_y) = load_data("train.txt") # 加载训练数据,调用load_data函数
(xs, ys), (o_x, o_y) = load_data("train.txt") # 加载训练数据,调用load_data函数
ndim = xs.shape[1] # 获取特征维度

model = LinearModel(ndim=ndim) # 实例化线性模型


#训练以及评估
# 训练以及评估
optimizer = optimizers.Adam(0.1)


@tf.function
def train_one_step(model, xs, ys):
# 在梯度带(GradientTape)上下文中记录前向计算过程
with tf.GradientTape() as tape:
y_preds = model(xs) # 模型前向传播计算预测值
loss = tf.keras.losses.MSE(ys, y_preds) #计算损失函数
grads = tape.gradient(loss, model.w) # 计算损失函数对模型参数w的梯度
optimizer.apply_gradients([(grads, model.w)]) # 更新模型参数
return loss # 返回模型的预测结果,即模型对输入数据 xs 的输出
y_preds = model(xs) # 模型前向传播计算预测值
loss = tf.keras.losses.MSE(ys, y_preds) # 计算损失函数
# 优化点2:修复梯度更新,同时计算w和b的梯度(原代码仅更新w)
grads = tape.gradient(loss, [model.w, model.b]) # 同时计算w和b的梯度
optimizer.apply_gradients(zip(grads, [model.w, model.b])) # 同时更新w和b
return loss # 返回模型的损失值


# 使用@tf.function装饰器将Python函数转换为TensorFlow图,以提高执行效率
Expand All @@ -155,21 +148,21 @@ def predict(model, xs):

def evaluate(ys, ys_pred):
"""评估模型的性能"""
return np.std(ys - ys_pred) # 计算预测误差的标准差
return np.std(ys - ys_pred) # 计算预测误差的标准差


# 评估指标的计算
for i in range(1000): # 进行1000次训练迭代
loss = train_one_step(model, xs, ys) # 执行单步训练并获取当前损失值
if i % 100 == 1: # 每100步打印一次损失值(从第1步开始:1, 101, 201, ...)
for i in range(1000): # 进行1000次训练迭代
loss = train_one_step(model, xs, ys) # 执行单步训练并获取当前损失值
if i % 100 == 1: # 每100步打印一次损失值(从第1步开始:1, 101, 201, ...)
print(f"loss is {loss:.4}") # `:.4` 表示保留4位有效数字

# 使用模型对训练集数据进行预测
y_preds = predict(model, xs)
# 打印测试集预测值与真实值的标准差
# 计算训练集预测值与真实值的标准差
std = evaluate(ys, y_preds)
# 打印训练集预测值与真实值的标准差
print("训练集预测值与真实值的标准差:{:.1f}".format(std)) # 格式化输出标准差,保留一位小数
print("训练集预测值与真实值的标准差:{:.1f}".format(std)) # 格式化输出标准差,保留一位小数

# 加载测试集数据
(xs_test, ys_test), (o_x_test, o_y_test) = load_data("test.txt")
Expand All @@ -182,21 +175,15 @@ def evaluate(ys, ys_pred):
print("测试集预测值与真实值的标准差:{:.1f}".format(std))

# 绘制原始数据点:红色圆点标记,大小3
# o_x: 原始数据X坐标
# o_y: 原始数据Y坐标
# "ro": 红色(r)圆形(o)标记
plt.plot(o_x, o_y, "ro", markersize=3)
plt.plot(o_x, o_y, "ro", markersize=3)
# 绘制模型预测曲线:黑色实线
# o_x_test: 测试集X坐标
# y_test_preds: 模型在测试集上的预测结果
# "k": 黑色(k)实线(默认线型)
plt.plot(o_x_test, y_test_preds, "k")
# 设置x、y轴标签
plt.xlabel("x")
plt.ylabel("y")
plt.title("Linear Regression") # 图表标题
plt.title("Linear Regression") # 图表标题
# 虚线网格,半透明灰色
plt.grid(True, linestyle="--", alpha=0.7, color="gray")
plt.legend(["train", "test", "pred"]) # 添加图例,元素依次对应
plt.legend(["train", "pred"]) # 修正图例(原图例多了"test",实际只有train和pred)
plt.tight_layout() # 自动调整布局
plt.show()# 显示图形
plt.show() # 显示图形
14 changes: 7 additions & 7 deletions src/chap03_SVM/svm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ def load_data(fname):
"""载入数据。"""
# 检查文件是否存在,确保数据加载的可靠性
if not os.path.exists(fname):
raise FileNotFoundError(f"数据文件未找到: {fname}\n请确认文件路径是否正确,当前工作目录为: {os.getcwd()}") # 如果文件不存在,抛出异常
raise FileNotFoundError(f"数据文件未找到: {fname}\
请确认文件路径是否正确,当前工作目录为: {os.getcwd()}") # 如果文件不存在,抛出异常
with open(fname, 'r') as f: # 打开文件
data = [] # 初始化一个空列表,用于存储数据
line = f.readline() # 跳过表头行
Expand Down Expand Up @@ -73,9 +74,9 @@ def train(self, data_train):
# 计算梯度:正则化项梯度 + 误分类样本梯度
# L2正则化:减小权重,防止过拟合
# hinge loss梯度:只对误分类和边界样本计算梯度
dw = (2 * self.reg_lambda * self.w) - np.sum(y[idx, None] * X[idx], axis=0) / m if len(
idx) > 0 else 2 * self.reg_lambda * self.w
db = -np.mean(y[idx]) if len(idx) > 0 else 0
# 优化:给 y 增加一个轴以匹配 X 的维度,解决广播错误,确保梯度计算可运行
dw = (2 * self.reg_lambda * self.w) - np.mean(y[idx, np.newaxis] * X[idx], axis=0)
db = -np.mean(y[idx])

# 梯度下降更新参数
self.w -= self.learning_rate * dw # 权重更新:w = w - η*dw/dw
Expand All @@ -97,7 +98,7 @@ def predict(self, x):
3. 距离为负 -> 预测为负类(0)
"""
score = np.dot(x, self.w) + self.b # 计算决策函数值
return (score >= 0).astype(np.int32) # 更简洁高效的布尔转整数方法
return np.where(score >= 0, 1, 0) # 转换回{0, 1}标签格式

if __name__ == '__main__':
# 数据加载部分以及数据路径配置
Expand Down Expand Up @@ -129,5 +130,4 @@ def predict(self, x):
acc_test = eval_acc(t_test, t_test_pred) # 测试集准确率

print("train accuracy: {:.1f}%".format(acc_train * 100)) # 输出训练集准确率
print("test accuracy: {:.1f}%".format(acc_test * 100)) # 输出测试集准确率

print("test accuracy: {:.1f}%".format(acc_test * 100)) # 输出测试集准确率
6 changes: 2 additions & 4 deletions src/chap03_softmax_regression/logistic_regression-exercise.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,8 @@ def compute_loss(pred, label):
# 计算所有样本损失的平均值
loss = tf.reduce_mean(losses)

# 将预测概率大于0.5的设置为1,小于等于0.5的设置为0,得到预测标签
pred = tf.where(pred > 0.5, tf.ones_like(pred), tf.zeros_like(pred))
# 计算预测标签与真实标签相等的比例,即准确率
accuracy = tf.reduce_mean(tf.cast(tf.equal(label, pred), dtype = tf.float32))
# 优化:直接使用 tf.round 四舍五入进行二分类判别,替代冗余的 where/ones_like/zeros_like 操作
accuracy = tf.reduce_mean(tf.cast(tf.equal(label, tf.round(pred)), dtype=tf.float32))
# 返回计算得到的损失值和准确率
return loss, accuracy

Expand Down
4 changes: 2 additions & 2 deletions src/chap03_softmax_regression/softmax_regression-exercise.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ def train_one_step(model, optimizer, x_batch, y_batch):

model = SoftmaxRegression()
# 创建一个 SoftmaxRegression 模型实例 model
opt = tf.keras.optimizers.SGD(learning_rate=0.01)
# 创建随机梯度下降(SGD)优化器实例 opt,设置学习率为 0.01
# 将 SGD 更换为 Adam 优化器,收敛速度更快,分类边界更稳定
opt = tf.keras.optimizers.Adam(learning_rate=0.02)

x1, x2, y = list(zip(*data_set)) #从data_set中提取特征和标签,并将它们分别存储到x1、x2 和 y 中
# 转换为 float32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,9 @@ def backward(self, grad_y):
class myModel:
def __init__(self):# 初始化模型参数,使用随机正态分布初始化权重矩阵
# 权重矩阵包含偏置项,通过增加输入特征维度实现
self.W1 = np.random.normal(size=[28 * 28 + 1, 100]) # 输入层到隐藏层,增加偏置项,W1: 连接输入层(784+1)和隐藏层(100)的权重矩阵
self.W2 = np.random.normal(size=[100, 10]) # 输入层到隐藏层,增加偏置项,W2: 连接隐藏层(100)和输出层(10)的权重矩阵
# 优化:添加 scale=0.1。原代码默认标准差为1.0,会导致初始权重过大,使Softmax饱和、梯度消失,模型难以训练。
self.W1 = np.random.normal(scale=0.1, size=[28 * 28 + 1, 100])
self.W2 = np.random.normal(scale=0.1, size=[100, 10])
# 初始化各层操作对象
self.mul_h1 = Matmul() # 第一个矩阵乘法层(输入到隐藏层)
self.mul_h2 = Matmul() # 第二个矩阵乘法层(隐藏层到输出层)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ def __call__(self, x):

model = MyModel()

# 使用Adam优化器,用于训练过程中更新模型参数
optimizer = optimizers.Adam()
# 优化:针对全量数据训练(Full Batch),默认的0.001学习率太慢,改为0.01能显著加快收敛
optimizer = optimizers.Adam(learning_rate=0.01)

# ## 计算 loss

Expand Down
Loading