【深度学习】使用其他深度学习框架(TensorFlow、PyTorch)实现波士顿房价预测任务
深度学习框架
前面学习了使用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.8
offset = 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_values
def show_plt(origin, predict, x_index=None):
if not x_index:
N = origin.shape[0] # 数据点的数量
x_index = np.arange(N)
# 绘制原始数据 origin
plt.plot(x_index, origin, color='blue', label='Original Data', alpha=0.6, linewidth=1)
# 绘制预测数据 predict
plt.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 x
def 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设置为10
mini_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_data
if __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_data
class 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.bias
return z
def loss(self, z, y):
error = z - y
num_samples = error.shape[0]
cost = error * error
cost = np.sum(cost) / num_samples
return cost
def 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_b
def update(self, gradient_w, gradient_b, eta=0.01):
self.weights = self.weights - eta * gradient_w
self.bias = self.bias - eta * gradient_b
def 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 losses
def predict(self, x):
return np.dot(x, self.weights) + self.bias
def 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 列作为 X
x = train_data[:, :-1] # 所有行,列从第 0 列到倒数第 2 列
# 提取最后一列作为 Y
y = 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四种方式实现波士顿房价预测任务,对于训练的结果都感觉不是很好,也不知道是数据量、模型设计、训练配置哪个环节出的问题。