diff --git a/code/bp_neural_network-wine_quality.py b/code/bp_neural_network-wine_quality.py index c3bf81a..d602b99 100644 --- a/code/bp_neural_network-wine_quality.py +++ b/code/bp_neural_network-wine_quality.py @@ -1,67 +1,30 @@ -import numpy as np +import torch +import torch.nn as nn +import torch.optim as optim from ucimlrepo import fetch_ucirepo from sklearn.model_selection import KFold from sklearn.metrics import accuracy_score from sklearn.preprocessing import StandardScaler - +import numpy as np # 定义BP神经网络类 -class BPNeuralNetwork: - def __init__(self, input_size, hidden_size, output_size, learning_rate=0.1): +class BPNeuralNetwork(nn.Module): + def __init__(self, input_size, hidden_size, output_size): + super(BPNeuralNetwork, self).__init__() # 初始化输入层、隐藏层和输出层的大小 self.input_size = input_size self.hidden_size = hidden_size self.output_size = output_size - self.learning_rate = learning_rate # 初始化权重和偏置 - self.weights_input_hidden = np.random.randn(self.input_size, self.hidden_size) # 输入层到隐藏层的权重 - self.bias_hidden = np.random.randn(self.hidden_size) # 隐藏层的偏置 - self.weights_hidden_output = np.random.randn(self.hidden_size, self.output_size) # 隐藏层到输出层的权重 - self.bias_output = np.random.randn(self.output_size) # 输出层的偏置 + self.fc1 = torch.nn.Linear(self.input_size, self.hidden_size) # 输入层到隐藏层的全连接层 + self.fc2 = torch.nn.Linear(self.hidden_size, self.output_size) # 隐藏层到输出层的全连接层 - def sigmoid(self, x): - # Sigmoid激活函数 - return 1 / (1 + np.exp(-x)) - - def sigmoid_derivative(self, x): - # Sigmoid激活函数的导数 - return x * (1 - x) - - def forward(self, X): + def forward(self, x): # 前向传播 - self.hidden_input = np.dot(X, self.weights_input_hidden) + self.bias_hidden # 隐藏层的输入 - self.hidden_output = self.sigmoid(self.hidden_input) # 隐藏层的输出 - self.final_input = np.dot(self.hidden_output, self.weights_hidden_output) + self.bias_output # 输出层的输入 - self.final_output = self.sigmoid(self.final_input) # 输出层的输出 - return self.final_output - - def backward(self, X, y, output): - # 反向传播 - output_error = y - output # 输出层的误差 - output_delta = output_error * self.sigmoid_derivative(output) # 输出层的误差信号 - - hidden_error = output_delta.dot(self.weights_hidden_output.T) # 隐藏层的误差 - hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden_output) # 隐藏层的误差信号 - - # 更新权重和偏置 - self.weights_hidden_output += self.hidden_output.T.dot(output_delta) * self.learning_rate # 更新隐藏层到输出层的权重 - self.bias_output += np.sum(output_delta, axis=0) * self.learning_rate # 更新输出层的偏置 - self.weights_input_hidden += X.T.dot(hidden_delta) * self.learning_rate # 更新输入层到隐藏层的权重 - self.bias_hidden += np.sum(hidden_delta, axis=0) * self.learning_rate # 更新隐藏层的偏置 - - def train(self, X, y, epochs): - # 训练网络 - for epoch in range(epochs): - output = self.forward(X) # 前向传播 - self.backward(X, y, output) # 反向传播 - if epoch % 1000 == 0: - loss = np.mean(np.square(y - output)) # 计算损失 - print(f'Epoch {epoch}, Loss: {loss}') # 打印损失 - - def predict(self, X): - # 预测 - return np.round(self.forward(X)) # 四舍五入预测结果 + x = torch.sigmoid(self.fc1(x)) # 隐藏层的输出 + x = torch.sigmoid(self.fc2(x)) # 输出层的输出 + return x # 将标签转换为one-hot编码 def one_hot_encode(y): @@ -72,7 +35,7 @@ def one_hot_encode(y): # 将标签转换为整数 y_int = np.array([label_to_int[label] for label in y]) n_values = np.max(y_int) + 1 - return np.eye(n_values)[y_int] # 返回one-hot编码 + return torch.eye(n_values)[y_int] # 返回one-hot编码 # fetch dataset wine_quality = fetch_ucirepo(id=186) @@ -81,12 +44,6 @@ wine_quality = fetch_ucirepo(id=186) X = wine_quality.data.features.values # 特征数据 y = wine_quality.data.targets.values # 标签数据 -# metadata -print(wine_quality.metadata) - -# variable information -print(wine_quality.variables) - # 特征缩放 scaler = StandardScaler() X = scaler.fit_transform(X) # 标准化特征数据 @@ -94,6 +51,11 @@ X = scaler.fit_transform(X) # 标准化特征数据 # 对标签进行one-hot编码 y_encoded = one_hot_encode(y) +# 将数据转换为PyTorch张量并移至GPU +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +X = torch.tensor(X, dtype=torch.float32).to(device) +y_encoded = y_encoded.to(device) + # 十折交叉验证 kf = KFold(n_splits=10, shuffle=True, random_state=42) accuracies = [] @@ -103,12 +65,24 @@ for train_index, test_index in kf.split(X): y_train, y_test = y_encoded[train_index], y_encoded[test_index] # 训练集和测试集的标签数据 # 创建并训练BP神经网络 - nn = BPNeuralNetwork(input_size=X_train.shape[1], hidden_size=20, output_size=y_train.shape[1], learning_rate=0.0001) # 修改隐藏层大小和学习率 - nn.train(X_train, y_train, epochs=50000) # 增加训练轮数 + nn = BPNeuralNetwork(input_size=X_train.shape[1], hidden_size=20, output_size=y_train.shape[1]).to(device) # 修改隐藏层大小 + criterion = torch.nn.MSELoss() # 使用均方误差损失函数 + optimizer = optim.SGD(nn.parameters(), lr=0.0001) # 使用随机梯度下降优化器 + + for epoch in range(50000): # 增加训练轮数 + optimizer.zero_grad() # 清零梯度 + output = nn(X_train) # 前向传播 + loss = criterion(output, y_train) # 计算损失 + loss.backward() # 反向传播 + optimizer.step() # 更新权重和偏置 + + if epoch % 1000 == 0: + print(f'Epoch {epoch}, Loss: {loss.item()}') # 打印损失 # 预测并计算准确率 - predictions = nn.predict(X_test) - accuracy = accuracy_score(np.argmax(y_test, axis=1), np.argmax(predictions, axis=1)) # 计算准确率 + with torch.no_grad(): + predictions = nn(X_test) + accuracy = accuracy_score(torch.argmax(y_test, dim=1).cpu().numpy(), torch.argmax(predictions, dim=1).cpu().numpy()) # 计算准确率 accuracies.append(accuracy) # 存储每次交叉验证的准确率 -print(f'Average Accuracy: {np.mean(accuracies)}') # 打印平均准确率 +print(f'Average Accuracy: {np.mean(accuracies)}') # 打印平均准确率 \ No newline at end of file