赞
踩
今天我们来聊聊神经网络中的两种重要的前馈网络模型:多层感知器(MLP)和卷积神经网络(CNN)。虽然它们都基于相同的原理,但它们在设计和应用上有着不同的侧重点,分别适用于不同的任务。
多层感知器(MLP)
多层感知器是神经网络的基础,它由多个感知器层(称为神经元)组成,每个神经元都是一个线性变换后接一个非线性激活函数。MLP可以学习复杂的非线性关系,是许多复杂任务的基础模型。
MLP的特点
* **结构简单**:MLP由线性层和激活函数层交替堆叠而成。
* **易于实现**:可以使用深度学习框架(如PyTorch)轻松实现MLP。
* **通用性强**:MLP可以应用于各种分类和回归任务。
我们来看一个简单的MLP实现:
import torch.nn as nn
import torch.nn.functional as F
class MultilayerPerceptron(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(MultilayerPerceptron, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, output_dim)
def forward(self, x_in, apply_softmax=False):
intermediate = F.relu(self.fc1(x_in))
output = self.fc2(intermediate)
if apply_softmax:
output = F.softmax(output, dim=1)
return output
这个MLP有两个线性层,中间使用ReLU激活函数。最后一层可以选择输出softmax概率。
MLP的局限性
* **过拟合风险**:MLP容易过拟合,需要正则化方法来防止。
* **训练复杂度**:MLP的训练过程可能比CNN复杂。
卷积神经网络(CNN)
卷积神经网络是一种特殊的神经网络,它通过卷积操作提取输入数据的局部特征。CNN在图像识别、语音识别和自然语言处理等领域取得了显著的成果。
**CNN的特点**:
* **局部感知**:CNN学习局部特征,能够识别图像中的物体部分。
* **参数共享**:CNN中的卷积核在整个网络中共享,减少了参数数量。
* **平移不变性**:CNN可以识别平移后的物体。
我们来看一个简单的CNN实现
import torch.nn as nn
import torch.nn.functional as F
class SurnameClassifier(nn.Module):
def __init__(self, initial_num_channels, num_classes, num_channels):
super(SurnameClassifier, self).__init__()
self.convnet = nn.Sequential(
nn.Conv1d(in_channels=initial_num_channels, out_channels=num_channels, kernel_size=3),
nn.ELU(),
nn.Conv1d(in_channels=num_channels, out_channels=num_channels, kernel_size=3, stride=2),
nn.ELU(),
# ... more conv layers
)
self.fc = nn.Linear(num_channels, num_classes)
def forward(self, x_surname, apply_softmax=False):
features = self.convnet(x_surname).squeeze(dim=2)
prediction_vector = self.fc(features)
if apply_softmax:
prediction_vector = F.softmax(prediction_vector, dim=1)
return prediction_vector
这个CNN模型使用了一系列的1D卷积层,提取了字符级的特征,最后接一个全连接层得到分类结果。
**CNN的应用**:
* **图像识别**:识别图像中的物体、场景和人脸。
* **目标检测**:检测图像中的物体位置和类别。
* **语音识别**:识别语音中的单词和短语。
* **自然语言处理**:识别文本中的实体和关系。
MLP和CNN各有优缺点,以下是它们之间的主要区别:
MLP vs. CNN |
| 特点 | MLP | |CNN | |
| 结构 | 多层感知器层 卷积层、池化层、全连接层 | |
| 特征提取 | 非局部特征 局部特征 | |
| 参数数量 | 较多 | 较少 | |
| 训练复杂度 | 较高 | 较低 | |
| 应用 | 通用分类和回归任务 | 图像识别、语音识别、自然语言处理 | |
### 总结
MLP和CNN是神经网络中两种重要的前馈网络模型,它们在结构和应用上有着不同的侧重点。MLP适用于通用分类和回归任务,而CNN适用于图像识别、语音识别和自然语言处理等领域。选择合适的模型取决于具体的应用场景和任务需求。
理解每种神经网络层对输入数据张量的大小和形状的影响,对于深入理解模型的行为至关重要。让我们通过一些具体的例子来详细说明这一点:
这是示例代码
import torch
from torch.nn import Conv1d
batch_size = 2
one_hot_size = 10
sequence_width = 7
data = torch.randn(batch_size, one_hot_size, sequence_width)
conv1 = Conv1d(in_channels=one_hot_size, out_channels=16,
kernel_size=3)
intermediate1 = conv1(data)
print(data.size())
print(intermediate1.size())
conv2 = nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3)
conv3 = nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3)
intermediate2 = conv2(intermediate1)
intermediate3 = conv3(intermediate2)
print(intermediate2.size())
print(intermediate3.size())
理解这些形状变换非常重要,因为它们反映了神经网络在不同层次上对输入数据的编码方式。全连接层引入了全连接的变换,卷积层则捕获了局部空间模式,池化层降低了分辨率,使特征更加鲁棒。监控张量形状变化可以帮助调试模型,验证层操作是否符合预期。这种对底层计算的深入理解,是构建和理解复杂神经网络模型的关键。
在每次卷积中,通道维数的大小都会增加,因为通道维数是每个数据点的特征向量。张量实际上是一个特征向量的最后一步是去掉讨厌的尺寸=1维。
另外还有两种方法可以将张量简化为每个数据点的一个特征向量:将剩余的值压平为特征向量,并在额外维度上求平均值。这两种方法如示例4-16所示。使用第一种方法,只需使用PyTorch的view()方法将所有向量平展成单个向量。第二种方法使用一些数学运算来总结向量中的信息。最常见的操作是算术平均值,但沿feature map维数求和和使用最大值也是常见的。每种方法都有其优点和缺点。扁平化保留了所有的信息,但会导致比预期(或计算上可行)更大的特征向量。平均变得与额外维度的大小无关,但可能会丢失信息。
示例代码:
# Method 2 of reducing to feature vectors
print(intermediate1.view(batch_size, -1).size())
# Method 3 of reducing to feature vectors
print(torch.mean(intermediate1, dim=2).size())
# print(torch.max(intermediate1, dim=2).size())
# print(torch.sum(intermediate1, dim=2).size())
接下来,我们将探讨如何使用卷积神经网络(CNN)对姓氏数据集进行分类。CNN已成为处理序列数据(如文本)的有效方法之一。让我们从数据集开始。
为了使用CNN对姓氏进行分类,我们需要将每个姓氏表示为矩阵。每个字符将映射到一个one-hot向量,并将这些向量组合成一个矩阵。矩阵的每一列对应于姓氏中的一个字符位置。
class SurnameVectorizer(object):
def vectorize(self, surname):
"""
Args:
surname (str): 姓氏
Returns:
one_hot_matrix (np.ndarray): 由one-hot向量组成的矩阵
"""
one_hot_matrix_size = (len(self.character_vocab), self.max_surname_length)
one_hot_matrix = np.zeros(one_hot_matrix_size, dtype=np.float32)
for position_index, character in enumerate(surname):
character_index = self.character_vocab.lookup_token(character)
one_hot_matrix[character_index][position_index] = 1
return one_hot_matrix
现在让我们看看用于分类的CNN模型。它由一系列Conv1D层和一个全连接层组成
class SurnameClassifier(nn.Module):
def __init__(self, initial_num_channels, num_classes, num_channels):
super().__init__()
self.convnet = nn.Sequential(
nn.Conv1d(in_channels=initial_num_channels, out_channels=num_channels, kernel_size=3),
nn.ELU(),
nn.Conv1d(in_channels=num_channels, out_channels=num_channels, kernel_size=3, stride=2),
nn.ELU(),
nn.Conv1d(in_channels=num_channels, out_channels=num_channels, kernel_size=3, stride=2),
nn.ELU(),
nn.Conv1d(in_channels=num_channels, out_channels=num_channels, kernel_size=3),
nn.ELU()
)
self.fc = nn.Linear(num_channels, num_classes)
def forward(self, x_surname, apply_softmax=False):
features = self.convnet(x_surname).squeeze(dim=2)
prediction_vector = self.fc(features)
if apply_softmax:
prediction_vector = F.softmax(prediction_vector, dim=1)
return prediction_vector
在forward函数中,Conv1D层被应用于输入矩阵。这些层能够学习输入序列中的局部模式。经过一系列卷积后,我们得到一个特征向量,该向量被输入到全连接层以生成预测向量。
为了训练模型,我们遵循标准的训练程序:
# 初始化数据集
surname_dataset = SurnameDataset.load_dataset_and_make_vectorizer(args.surname_csv)
train_dataset = surname_dataset.train_data
vectorizer = surname_dataset.vectorizer
# 初始化模型
model=SurnameClassifier(initial_num_channels=vectorizer.alphabet_size, num_classes=vectorizer.num_nationalities, num_channels=args.num_channels)
model = model.to(device)
# 初始化损失函数和优化器
loss_func = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=args.learning_rate)
# 训练模型
for epoch in range(args.num_epochs):
...
model.train()
for batch in train_data:
...
optimizer.zero_grad()
y_pred = model(x_surname)
loss = loss_func(y_pred, y_nationality)
loss.backward()
optimizer.step()
# 在验证集上评估模型
model.eval()
val_losses = []
最后,我们可以使用训练好的模型对新的姓氏进行预测。
def predict_nationality(surname, classifier, vectorizer):
vectorized_surname = vectorizer.vectorize(surname)
vectorized_surname = torch.tensor(vectorized_surname).unsqueeze(0)
result = classifier(vectorized_surname, apply_softmax=True)
probability_values, indices = result.max(dim=1)
index = indices.item()
predicted_nationality = vectorizer.nationality_vocab.lookup_index(index)
probability_value = probability_values.item()
return {'nationality': predicted_nationality, 'probability': probability_value}
# 示例用法
surname = "Kernitzki"
prediction = predict_nationality(surname, model, vectorizer)
print(f"Surname: {surname}, Predicted Nationality: {prediction['nationality']}, Probability: {prediction['probability']:.3f}")
本篇博客讨论了卷积神经网络(CNN)在自然语言处理任务中的应用,以姓氏分类为例。主要内容包括:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。