0%

神经网络分类任务综合实验

实验目的

  1. 熟悉和掌握机器学习的完整流程
  2. 熟悉和掌握神经网络的构建

实验要求

  1. 采用Python、Matlab等高级语言进行编程,推荐优先选用Python语言
  2. 代码可读性强:变量、函数、类等命名可读性强,包含必要的注释

实验原理

人工神经网络(ANN),简称神经网络,是一种模仿生物神经网络的结构和功能的数学模型或计算模型。神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统。现代神经网络是一种非线性统计性数据建模工具,常用来对输入和输出间复杂的关系进行建模,或用来探索数据的模式。
人工神经网络从以下四个方面去模拟人的智能行为:

  • 物理结构:人工神经元将模拟生物神经元的功能
  • 计算模拟:人脑的神经元有局部计算和存储的功能,通过连接构成一个系统。人工神经网络中也有大量有局部处理能力的神经元,也能够将信息进行大规模并行处理
  • 存储与操作:人脑和人工神经网络都是通过神经元的连接强度来实现记忆存储功能,同时为概括、类比、推广提供有力的支持
  • 训练:同人脑一样,人工神经网络将根据自己的结构特性,使用不同的训练、学习过程,自动从实践中获得相关知识

神经网络是一种运算模型,由大量的节点(或称“神经元”,或“单元”)和之间相互联接构成。每个节点代表一种特定的输出函数,称为激励函数。每两个节点间的连接都代表一个对于通过该连接信号的加权值,称之为权重,这相当于人工神经网络的记忆。网络的输出则依网络的连接方式,权重值和激励函数的不同而不同。而网络自身通常都是对自然界某种算法或者函数的逼近,也可能是对一种逻辑策略的表达。

感知器

历史上,科学家一直希望模拟人的大脑,造出可以思考的机器。人为什么能够思考?科学家发现,原因在于人体的神经网络。

alt text

既然思考的基础是神经元,如果能够”人造神经元”(artificial neuron),就能组成人工神经网络,模拟思考。上个世纪六十年代,提出了最早的”人造神经元”模型,叫做”感知器”(perceptron),直到今天还在用。

alt text

上图的圆圈就代表一个感知器。它接受多个输入(x1,x2,x3…),产生一个输出(output),好比神经末梢感受各种外部环境的变化,最后产生电信号。

为了简化模型,我们约定每种输入只有两种可能:1 或 0。如果所有输入都是1,表示各种条件都成立,输出就是1;如果所有输入都是0,表示条件都不成立,输出就是0

决策模型

单个的感知器构成了一个简单的决策模型,已经可以拿来用了。真实世界中,实际的决策模型则要复杂得多,是由多个感知器组成的多层网络。

alt text

上图中,底层感知器接收外部输入,做出判断以后,再发出信号,作为上层感知器的输入,直至得到最后的结果。(注意:感知器的输出依然只有一个,但是可以发送给多个目标。)

一个神经网络的搭建,需要满足三个条件。

  • 输入和输出
  • 权重(W)和阈值(b)
  • 多层感知机的结构

神经网络的运作过程如下:

确定输入和输出
1. 找到一种或多种算法,可以从输入得到输出
2. 找到一组已知答案的数据集,用来训练模型,估算W和b
3. 一旦新的数据产生,输入模型,就可以得到结果,同时对W和b进行校正

可以看到,整个过程需要海量计算。所以,神经网络直到最近这几年才有实用价值,而且一般的 CPU 还不行,要使用专门为机器学习定制的 GPU 来计算。

实验内容

数据简介

采用数据集 “data/positive.csv”和“data/negative.csv”进行本次实验。

原始数据可视化

调用可视化工具,将原始数据可视化输出至二维平面内,以颜色区分不同类别。(python可使用UMAP,conda install umap-learn;matlab可使用t-sne函数)。

模型设计

  1. 本实验为二分类问题,正负样本各200个。
  2. 使用BP神经网络进行二分类(Python可使用Pytorch,MATLAB可使用神经网络工具箱newff函数。
  3. 设计包含3个隐藏层的BP神经网络,输入层维度为样本维度;隐藏层维度分别是128,32,2,隐藏层使用relu激活函数;输出层维度1,使用sigmoid激活函数。

模型训练与性能评估

  1. 使用五折交叉验证(5-fold cross-validation)评估神经网络在该数据上的性能。
  2. 五折交叉验证:将正负样本各分为数量相等的5份:1-40,41-80,81-120,121-160,161-200,并把1份正样本与1份负样本合并构成1个子集,则原数据分成了5个子集,且子集中正负样本比例1:1保持不变。对模型进行5次训练测试:
    • 第i次时,选择第i份子集作为测试集,其余4份数据子集作为训练集。使用训练集训练数据,并使用测试集验证/测试性能,计算测试集的AUROC值。
    • 模型倒数第2层维度为2,导出测试集数据在这一层上的数值,输出成二维散点图,包含两个子图:子图1的标签为测试样本的预测标签(取决于模型输出,将输出大于0.5的样本预测为正样本,否则预测为负样本),以不同颜色区分不同的预测结果;子图2的标签为测试样本的真实标签,以不同颜色区分不同的真实结果。
    • 完成5次,使得每一个子集都作为测试集1次,作为训练集4次。计算整体的AUROC。
  3. 打断正负样本排列,重新划分训练集与测试集,重新训练模型,查看性能变化。

特征选择和特征提取

  1. 特征选择:针对原始数据,计算每个特征的方差,选取两个方差最大的特征。将正负样本点输出为二维散点图,以颜色区分。
  2. 特征提取:针对原始数据,调用PCA降维函数将数据降到二维,将正负样本点输出为二维散点图,以颜色区分。

代码输出:

  • 原始数据的散点图;
  • 测试集的散点图;
  • 经过特征选择后的散点图;
  • 经过PCA降维后的散点图。

实验代码和结果

1
2
3
4
5
6
7
8
po = pd.read_csv(r'E:\firefox download\positive.csv',sep=',',header=None)
ne=pd.read_csv(r'E:\firefox download\negative.csv',sep=',',header=None)
poe = umap.UMAP(n_components=2).fit_transform(po)
nee= umap.UMAP(n_components=2).fit_transform(ne)
plt.scatter(poe[:, 0], poe[:, 1],label="positive")
plt.scatter(nee[:, 0], nee[:, 1],label="negative")
plt.legend()
plt.show()

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import pandas as pd
from sklearn.metrics import roc_auc_score
import matplotlib.pyplot as plt
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import StandardScaler

# 定义神经网络模型
class NeuralNetwork(nn.Module):
def __init__(self, input_dim, hidden_dims):
super(NeuralNetwork, self).__init__()
self.input_layer = nn.Linear(input_dim, hidden_dims[0])
self.hidden_layers = nn.ModuleList([nn.Linear(hidden_dims[i], hidden_dims[i+1]) for i in range(len(hidden_dims)-1)])
self.output_layer = nn.Linear(hidden_dims[-1], 1)
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()

def forward(self, x):
x = self.relu(self.input_layer(x))
for layer in self.hidden_layers:
x = self.relu(layer(x))
x = self.sigmoid(self.output_layer(x))
return x

# 读取数据
positive_data = pd.read_csv(r'E:\firefox download\positive.csv',header=None)
negative_data = pd.read_csv(r'E:\firefox download\negative.csv',header=None)


# 合并数据
data = pd.concat([positive_data, negative_data], ignore_index=True,axis=1).T.values
label_train = np.concatenate([np.ones(200), np.zeros(200)])

# 存储每次的AUC值
auc_values = []

# 初始化模型
input_dim = data.shape[1]
hidden_dims = [128, 32, 2]
model = NeuralNetwork(input_dim, hidden_dims)

# 定义损失函数和优化器
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
skf=StratifiedKFold(n_splits=5,shuffle=True)
# 进行5次测试
for fold,(train_indices,test_indices) in enumerate(skf.split(data,label_train)):
print(f"Fold {fold + 1}:")


X_train, X_test = data[train_indices], data[test_indices]
y_train, y_test = label_train[train_indices], label_train[test_indices]

# Check if there are both positive and negative samples in the test set
if set(y_test) == {0} or set(y_test) == {1}:
print("Warning: Test set contains only one class. Skipping evaluation for this fold.")
continue

# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 转换为PyTorch的Tensor
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)

X_test_tensor = torch.tensor(X_test, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

# 训练模型
num_epochs = 1000
for epoch in range(num_epochs):
model.train()
optimizer.zero_grad()
outputs = model(X_train_tensor)
loss = criterion(outputs, y_train_tensor)
loss.backward()
optimizer.step()

# 在测试集上评估模型
model.eval()
with torch.no_grad():
test_outputs = model(X_test_tensor)
predicted_labels = (test_outputs >= 0.5).float()

# Check if there are both positive and negative predictions
if set(predicted_labels.numpy().flatten()) == {0} or set(predicted_labels.numpy().flatten()) == {1}:
print("Warning: Predictions contain only one class. Skipping evaluation for this fold.")
continue

# 计算AUC值
auc = roc_auc_score(y_test, predicted_labels.numpy())
print(f'AUC on test set: {auc:.4f}')
auc_values.append(auc)

# 绘制散点图
plt.figure(figsize=(8, 6))
plt.subplot(2, 1, 1)
plt.scatter(range(len(predicted_labels)), predicted_labels.numpy(), label=f'Fold {fold + 1}')
plt.title('Predicted Labels')

plt.subplot(2, 1, 2)
plt.scatter(range(len(y_test)), y_test, label=f'Fold {fold + 1}')
plt.title('True Labels')
plt.show()

plt.tight_layout()

# 计算整体的平均AUC值

mean_auc = sum(auc_values) / len(auc_values)
print(f'Mean AUC across folds: {mean_auc:.4f}')

alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
X = pd.read_csv(r'E:\firefox download\positive.csv',sep=',',header=None)
Y=pd.read_csv(r'E:\firefox download\negative.csv',sep=',',header=None)
X=X.values
Y=Y.values

# 计算每个特征的方差
X_variances = np.var(X, axis=0)
Y_variances = np.var(Y, axis=0)
# 选择两个方差最大的特征
X_features = X[:, np.argsort(X_variances)[-2:]]
Y_features = Y[:, np.argsort(Y_variances)[-2:]]

# 输出二维散点图
plt.scatter(X_features[:,0], X_features[:,1], color='blue', label='Positive Samples')
plt.scatter(Y_features[:,0], Y_features[:,1], color='red', label='Negative Samples')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('Feature Selection')
plt.legend()
plt.show()

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
X = pd.read_csv(r'E:\firefox download\positive.csv',sep=',',header=None)
Y=pd.read_csv(r'E:\firefox download\negative.csv',sep=',',header=None)
X=X.values
Y=Y.values

cov_matrix = np.cov(X, rowvar=False)

# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)

# 选择前两个特征向量对应的特征值最大的两个特征
X_features = np.dot(X, eigenvectors[:, -2:])
Y_features = np.dot(Y, eigenvectors[:, -2:])

# 输出二维散点图
plt.scatter(X_features[:,0], X_features[:,1], color='blue', label='Positive Samples')
plt.scatter(Y_features[:,0], Y_features[:,1], color='red', label='Negative Samples')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.title('Feature Extraction (PCA)')
plt.legend()
plt.show()

alt text

小结或讨论

显而易见的是,神经网络的分类方法能够适用于任何数据集,在模型搭建好之后,只需要进行训练,就可以得到不错的结果,训练的轮数越多,网络的层数越深,就能够得到约准确的分类,但是带来的时间开销也是巨大的。