深度学习框架
前面学习了使用numpy+python、paddle实现波士顿房价预测任务,为了更加深刻的了解深度学习步骤,决定再选择两种比较流行的其他深度学习框架进行比较。
我个人听到比较多的是TensorFlow、PyTorch,因此本文章按照前面的波士顿房价预测任务用TensorFlow、PyTorch来实现。
深度学习步骤
1、数据处理
2、模型设计
3、训练配置
4、训练过程
5、模型保存
6、模型推理
1.数据处理
我们暂时认为所有的开源框架对数据处理的代码都是一样的。数据处理包含六个部分:数据获取、数据导入、数据形状变换、数据集划分、数据归一化处理和封装load data函数。数据预处理后,才能被模型调用。
数据获取:百度飞桨提供很多数据集
https://aistudio.baidu.com/datasetoverview
def load_data():# 2、数据导入# 从文件导入数据datafile = './work/housing.data'data = np.fromfile(datafile, sep=' ')# 3、数据形状变换# 每条数据包括14项,其中前面13项是影响因素,第14项是相应的房屋价格中位数feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE','DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]feature_num = len(feature_names)# 将原始数据进行Reshape,变成[N, 14]这样的形状data = data.reshape([data.shape[0] // feature_num, feature_num])# 4、数据集划分# 将原数据集拆分成训练集和测试集# 这里使用80%的数据做训练,20%的数据做测试# 测试集和训练集必须是没有交集的ratio = 0.8offset = int(data.shape[0] * ratio)train_data = data[:offset]# 5、数据归一化处理# 计算训练集的最大值,最小值max_values, min_values = train_data.max(axis=0), train_data.min(axis=0)# 对数据进行归一化处理for i in range(feature_num):data[:, i] = (data[:, i] - min_values[i]) / (max_values[i] - min_values[i])# 使用训练集计算最大值和最小值# scaler = MinMaxScaler()# # 只在训练集上拟合# scaler.fit(train_data)# data = scaler.transform(data)# 训练集和测试集的划分比例train_data = data[:offset]test_data = data[offset:]return train_data, test_data, max_values, min_valuesdef show_plt(origin, predict, x_index=None):if not x_index:N = origin.shape[0] # 数据点的数量x_index = np.arange(N)# 绘制原始数据 originplt.plot(x_index, origin, color='blue', label='Original Data', alpha=0.6, linewidth=1)# 绘制预测数据 predictplt.plot(x_index, predict, color='red', label='Predicted Data', linewidth=2)# 添加标题和标签(注意:这里我们没有使用实际的 x 值作为横轴标签,因为只有 y 和 predict)plt.title('Comparison of Original Data and Predicted Data')plt.xlabel('Index') # 或者你可以使用 'Sample Number'、'Data Point' 等标签plt.ylabel('Value')plt.legend()# 显示图表plt.grid(True)plt.show()
2.PyTorch
波士顿房价预测任务使用的是线性回归模型,还是按照步骤一步步实现。
2.1.数据处理
2.2.模型设计
# 导入必要的库文件
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
# 数据处理中写的两个方法,一个加载数据,一个显示预测结果与原值的曲线图
from load_data import load_data, show_plt
# 定义模型
class Regressor(nn.Module):def __init__(self, input_size):super(Regressor, self).__init__()self.fc = nn.Linear(input_size, 1)def forward(self, x):x = self.fc(x)return x
2.3.训练配置
# 加载数据集
# 使用load_data加载训练集数据和测试集数据
train_data, test_data, max_values, min_values = load_data()
# 初始化模型
model = Regressor(13)
num_epochs = 20 # 设置模型训练轮次
batch_size = 10 # 设置批大小,即一次模型训练使用的样本数量
2.4.训练过程
同样在模型Regressor中定义一个训练方法,训练方法中定义了损失函数和优化器
class Regressor(nn.Module):def __init__(self, input_size):super(Regressor, self).__init__()self.fc = nn.Linear(input_size, 1)def forward(self, x):x = self.fc(x)return xdef fit_model(self, train_data, num_epochs, batch_size=10):# 定义损失函数和优化器criterion = nn.MSELoss()optimizer = optim.Adam(model.parameters(), lr=0.001)# 定义模型训练轮次epoch(外层循环)for epoch in range(num_epochs):# 对训练集数据进行拆分,batch_size设置为10mini_batches = [train_data[k:k + batch_size] for k in range(0, len(train_data), batch_size)]# 定义模型训练(内层循环)for iter_id, mini_batch in enumerate(mini_batches):# 前向传播x = np.array(mini_batch[:, :-1]) # 将当前批的房价影响因素的数据转换为np.array格式y = np.array(mini_batch[:, -1:]) # 将当前批的标签数据(真实房价)转换为np.array格式# 转换为 PyTorch 张量x = torch.tensor(x, dtype=torch.float32)y = torch.tensor(y, dtype=torch.float32).view(-1, 1)outputs = model(x)loss = criterion(outputs, y)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()if (epoch + 1) % 10 == 0:print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')
开始训练
model.fit_model(train_data, num_epochs, batch_size)
2.5 模型保存
# 保存模型
torch.save(model.state_dict(), 'boston_model.pth')
print("模型已保存到 boston_model.pth")
2.6 模型推理
def predict_pytorch():# 重新初始化模型loaded_model = pytorch_Regressor(13)# 加载模型的状态字典loaded_model.load_state_dict(torch.load('boston_model.pth'))# 设置模型为评估模式loaded_model.eval()x, y = load_example()x = torch.tensor(x, dtype=torch.float32)# 使用加载的模型进行预测with torch.no_grad():predict = loaded_model(x)predict = predict * (max_values[-1] - min_values[-1]) + min_values[-1]# # 对label数据进行后处理y = y * (max_values[-1] - min_values[-1]) + min_values[-1]show_plt(y.flatten(), predict.numpy().flatten())if __name__ == '__main__':# predict_paddle()predict_pytorch()
3.TensorFlow
有了pytorch的经验,对于TensorFlow就直接贴完整代码了:
在tensorflow_model.py中,用于训练模型
import os
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
from keras import layers, models
from data import load_dataif __name__ == '__main__':# 使用load_data加载训练集数据和测试集数据train_data, test_data, max_values, min_values = load_data()train_x = train_data[:, :-1]train_y = train_data[:, -1]# 定义模型model = models.Sequential([layers.Dense(1, input_shape=(train_x.shape[1],)) # 输出层,无激活函数(回归任务)])# 编译模型model.compile(optimizer='adam', loss='mse', metrics=['mae']) # 使用均方误差损失和平均绝对误差指标# 训练模型history = model.fit(train_x, train_y, epochs=100, batch_size=32, validation_split=0.01, verbose=1)# 保存模型model.save('tensorflow_model.keras')print("模型已保存到 tensorflow_model.keras")
在tensorflow_predict.py中,用于模型推理
import os
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
from keras import layers, models
from data import load_data, show_plt, load_example
train_data, test_data, max_values, min_values = load_data()def predict():# 将模型参数保存到指定路径中# 加载模型loaded_model = models.load_model('tensorflow_model.keras')# 使用加载的模型进行预测x, y = load_example()predict = loaded_model.predict(x).flatten()predict = predict * (max_values[-1] - min_values[-1]) + min_values[-1]# # 对label数据进行后处理y = y * (max_values[-1] - min_values[-1]) + min_values[-1]show_plt(y, predict)if __name__ == '__main__':predict()
4.python+numpy的完善
前面按照paddle的过程学习了使用 Python 和 NumPy 手动实现模型训练,但是过程中没有保存模型和进行模型推理,这里补充完整。
numpy_model.py
# 导入需要用到的package
import numpy as np
import matplotlib.pyplot as plt
from data import load_dataclass Network(object):def __init__(self, input_size):# 随机产生w的初始值# 为了保持程序每次运行结果的一致性,# 此处设置固定的随机数种子np.random.seed(0)self.weights = np.random.randn(input_size, 1)self.bias = 0.def forward(self, x):z = np.dot(x, self.weights) + self.biasreturn zdef loss(self, z, y):error = z - ynum_samples = error.shape[0]cost = error * errorcost = np.sum(cost) / num_samplesreturn costdef gradient(self, x, y):z = self.forward(x)N = x.shape[0]gradient_w = 1. / N * np.sum((z - y) * x, axis=0)gradient_w = gradient_w[:, np.newaxis]gradient_b = 1. / N * np.sum(z - y)return gradient_w, gradient_bdef update(self, gradient_w, gradient_b, eta=0.01):self.weights = self.weights - eta * gradient_wself.bias = self.bias - eta * gradient_bdef fit_model(self, train_data, num_epochs, batch_size=10, eta=0.01):n = len(train_data)losses = []for epoch_id in range(num_epochs):# 在每轮迭代开始之前,将训练数据的顺序随机打乱# 然后再按每次取batch_size条数据的方式取出np.random.shuffle(train_data)# 将训练数据进行拆分,每个mini_batch包含batch_size条的数据mini_batches = [train_data[k:k + batch_size] for k in range(0, n, batch_size)]for iter_id, mini_batch in enumerate(mini_batches):# print(self.w.shape)# print(self.b)x = mini_batch[:, :-1]y = mini_batch[:, -1:]a = self.forward(x)loss = self.loss(a, y)gradient_w, gradient_b = self.gradient(x, y)self.update(gradient_w, gradient_b, eta)losses.append(loss)print('Epoch {:3d} / iter {:3d}, loss = {:.4f}'.format(epoch_id, iter_id, loss))return lossesdef predict(self, x):return np.dot(x, self.weights) + self.biasdef save_model(self, filename):np.savez(filename, weights=self.weights, bias=self.bias)print(f"模型已保存到 {filename}.npz")if __name__ == '__main__':train_data, test_data, max_values, min_values = load_data()# 打乱样本顺序np.random.shuffle(train_data)# 获取数据# 提取前 13 列作为 Xx = train_data[:, :-1] # 所有行,列从第 0 列到倒数第 2 列# 提取最后一列作为 Yy = train_data[:, -1].reshape([len(train_data), 1]) # 所有行,最后一列net = Network(13)# 启动训练losses = net.fit_model(train_data, num_epochs=100, batch_size=20, eta=0.01)net.save_model('numpy_model.npz')
numpy_predict.py
# 导入需要用到的package
import numpy as np
from numpy_model import Network as numpy_Regressor
from data import load_data, show_plt, load_example
train_data, test_data, max_values, min_values = load_data()def predict():# 重新初始化模型file_name = 'numpy_model.npz'data = np.load(file_name)loaded_model = numpy_Regressor(input_size=13)loaded_model.weights = data['weights']loaded_model.bias = data['bias']print(f"模型已从 {file_name} 加载")x, y = load_example()# 使用加载的模型进行预测predict = loaded_model.predict(x)predict = predict * (max_values[-1] - min_values[-1]) + min_values[-1]# # 对label数据进行后处理y = y * (max_values[-1] - min_values[-1]) + min_values[-1]show_plt(y.flatten(), predict.flatten())if __name__ == '__main__':predict()
4.学习总结
1、matplotlib报错,按照如下方法解决
2、pytorch、paddle、tensorflow各框架在python依赖库可能会存在版本冲突,最好用conda分开
3、到目前为止,学习完了用python+numpy、paddle、pytorch、tensorflow四种方式实现波士顿房价预测任务,对于训练的结果都感觉不是很好,也不知道是数据量、模型设计、训练配置哪个环节出的问题。