1.激活函数
- 非线性
- 单调
- 连续
并不一定要求可导,比如
ReLU
就是连续但不可导,在不可导的点取左导数或右导数即可;
sigmoid:
sigmoid
存在的问题
- 梯度消失问题;
- 求导计算量大;
- 非对称;
2.损失函数
-
均方误差损失函数(Mean Squared Error,MSE):用于回归任务,衡量模型预测结果和实际结果之间的均方差。
-
交叉熵损失函数(Cross-Entropy,CE):用于分类任务,衡量模型预测结果和实际结果之间的差异,通常与softmax激活函数一起使用。
-
二分类交叉熵损失函数(Binary Cross-Entropy,BCE):用于二分类任务,与交叉熵损失函数类似,但只有两个类别。
-
对数损失函数(Log Loss):用于二分类任务,计算二分类问题的对数损失,通常与sigmoid激活函数一起使用。
-
Hinge Loss:用于支持向量机中,用于分类任务,能够更好地处理样本的不确定性。
-
Kullback-Leibler散度损失函数(KL散度):用于衡量两个概率分布之间的差异。
-
平衡误差损失函数(Focal Loss):用于解决类别不平衡问题,能够更好地处理少数类样本的误分类问题。
-
Dice Loss:用于图像分割任务中,衡量模型预测结果和实际结果之间的相似度。
-
Triplet Loss:用于人脸识别和图像检索任务中,用于学习嵌入空间中不同类别之间的距离。
import torch.nn as nn
# 均方误差损失函数
mse_loss = nn.MSELoss()
# 交叉熵损失函数
ce_loss = nn.CrossEntropyLoss()
# 二分类交叉熵损失函数
bce_loss = nn.BCEWithLogitsLoss()
# 对数损失函数
log_loss = nn.BCELoss()
# Hinge Loss
hinge_loss = nn.HingeEmbeddingLoss()
# Kullback-Leibler散度损失函数
kl_loss = nn.KLDivLoss()
# 平衡误差损失函数
focal_loss = FocalLoss()
# Dice Loss
dice_loss = DiceLoss()
# Triplet Loss
triplet_loss = nn.TripletMarginLoss()
3.过拟合和正则化
(1)过拟合:训练集效果很好,但是验证集很差,这种现象称为过拟合,表现为高方差。
常见解决方法
1.训练数据集不足问题
①数据增强
- NLP-EDA是一种基于词汇替换的文本数据增强方法,旨在增加训练数据的多样性和数量,以提高模型的泛化性能。NLP-EDA的主要思想是在原始文本中进行多种替换操作,生成新的文本样本。
具体来说,NLP-EDA包括四种替换操作:
a.同义词替换(Synonym Replacement):使用同义词替换原始文本中的某个词语,以保持句子意思的不变。
b.随机插入(Random Insertion):在原始文本中随机插入一个新的词语。
c.随机删除(Random Deletion):随机删除原始文本中的某个词语。
d.随机交换(Random Swap):随机交换原始文本中两个词语的位置。
import random
import nltk
from nltk.corpus import wordnet
# 初始化随机种子
random.seed(42)
# 同义词替换
def synonym_replacement(words, n):
new_words = words.copy()
for _ in range(n):
idx = random.randint(0, len(new_words) - 1)
word = new_words[idx]
synonyms = wordnet.synsets(word)
if synonyms:
synonym = random.choice(synonyms).lemmas()[0].name()
new_words[idx] = synonym
return new_words
# 随机插入
def random_insertion(words, n):
new_words = words.copy()
for _ in range(n):
idx = random.randint(0, len(new_words) - 1)
word = random.choice(list(wordnet.words()))
new_words.insert(idx, word)
return new_words
# 随机删除
def random_deletion(words, p):
new_words = words.copy()
for i in range(len(new_words)):
if random.uniform(0, 1) < p:
new_words.pop(i)
return new_words
# 随机交换
def random_swap(words, n):
new_words = words.copy()
for _ in range(n):
idx1 = random.randint(0, len(new_words) - 1)
idx2 = random.randint(0, len(new_words) - 1)
new_words[idx1], new_words[idx2] = new_words[idx2], new_words[idx1]
return new_words
# NLP-EDA数据增强
def nlp_eda(text, alpha_sr=0.1, alpha_ri=0.1, alpha_rd=0.1, alpha_rs=0.1, num_aug=9):
words = nltk.word_tokenize(text)
num_words = len(words)
augmented_sentences = set()
num_new_per_technique = int(num_aug / 4) + 1
# 同义词替换
for _ in range(num_new_per_technique):
a_words = synonym_replacement(words, int(alpha_sr * num_words))
augmented_sentences.add(' '.join(a_words))
# 随机插入
for _ in range(num_new_per_technique):
a_words = random_insertion(words, int(alpha_ri * num_words))
augmented_sentences.add(' '.join(a_words))
# 随机删除
for _ in range(num_new_per_technique):
a_words = random_deletion(words, alpha_rd)
augmented_sentences.add(' '.join(a_words))
# 随机交换
for _ in range(num_new_per_technique):
a_words = random_swap(words, int(alpha_rs * num_words))
augmented_sentences.add(' '.join(a_words))
return augmented_sentences
- CV-切割、裁剪、旋转:
-
切割(Cropping):随机从图像中切割出一个子区域,用于训练。这可以使模型更好地学习到图像中的局部特征,同时也可以增加训练样本的数量。
-
裁剪(Padding):在图像的边界添加一些填充,使其大小与原图像相同。这可以使模型在处理大小不同的图像时表现更好,同时也可以增加训练样本的数量。
-
旋转(Rotation):随机旋转图像的角度,可以增加模型对旋转图像的鲁棒性。
-
翻转(Flipping):随机水平或垂直翻转图像,可以增加模型对镜像图像的鲁棒性。
import numpy as np
import cv2
# 图像裁剪
def image_padding(image, size):
h, w = image.shape[:2]
if h > size or w > size:
return cv2.resize(image, (size, size))
else:
top_pad = (size - h) // 2
bottom_pad = size - h - top_pad
left_pad = (size - w) // 2
right_pad = size - w - left_pad
return cv2.copyMakeBorder(image, top_pad, bottom_pad, left_pad, right_pad, cv2.BORDER_CONSTANT)
# 图像切割
def random_crop(image, crop_size):
h, w = image.shape[:2]
if h == crop_size and w == crop_size:
return image
x = np.random.randint(0, w - crop_size)
y = np.random.randint(0, h - crop_size)
return image[y:y+crop_size, x:x+crop_size]
# 图像旋转
def random_rotate(image, max_angle):
h, w = image.shape[:2]
center = (w//2, h//2)
angle = np.random.uniform(-max_angle, max_angle)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
return rotated
- MIXUP:它可以通过对原始数据进行随机变换,从而生成更多的训练样本,增强模型的泛化能力。Mixup是一种数据增强技术,它是将两张不同的图像进行线性组合,从而生成一张新的图像。具体来说,对于两张图像I1和I2,我们随机生成一个权重因子λ∈[0,1],然后按照如下公式生成一张新的图像I:
- Mixup数据增强的主要思想是通过对两张不同的图像进行组合,生成新的训练样本,从而增加训练样本的数量,并且可以有效地防止过拟合。
import numpy as np
import tensorflow as tf
# Mixup数据增强
def mixup(images, labels, alpha=1.0):
batch_size = tf.shape(images)[0]
weight = tf.random.uniform(shape=(batch_size,), minval=0.0, maxval=1.0)
indices = tf.random.shuffle(tf.range(batch_size))
x1, x2 = images, tf.gather(images, indices)
y1, y2 = labels, tf.gather(labels, indices)
x = weight[:, tf.newaxis, tf.newaxis, tf.newaxis] * x1 + (1.0 - weight[:, tf.newaxis, tf.newaxis, tf.newaxis]) * x2
y = alpha * y1 + (1.0 - alpha) * y2
return x, y
②对抗训练:是一种有效的数据增强技术,可以在数据不足的情况下增强模型的泛化能力。其主要思想是在模型训练过程中,将生成的对抗样本作为训练数据,从而使得模型更加鲁棒。
import tensorflow as tf
import numpy as np
# 对抗训练增强数据
def adversarial_training(model, dataset, epsilon):
optimizer = tf.keras.optimizers.Adam()
for images, labels in dataset:
# 生成对抗样本
adv_images = generate_adversarial_images(model, images, labels, epsilon)
# 将对抗样本和原始样本合并
images = tf.concat([images, adv_images], axis=0)
labels = tf.concat([labels, labels], axis=0)
# 随机打乱数据
indices = tf.random.shuffle(tf.range(images.shape[0]))
images = tf.gather(images, indices)
labels = tf.gather(labels, indices)
# 计算损失并更新模型
with tf.GradientTape() as tape:
logits = model(images, training=True)
loss_value = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits))
gradients = tape.gradient(loss_value, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
# 生成对抗样本
def generate_adversarial_images(model, images, labels, epsilon):
with tf.GradientTape() as tape:
tape.watch(images)
logits = model(images, training=True)
loss_value = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits))
gradients = tape.gradient(loss_value, images)
signed_grad = tf.sign(gradients)
adv_images = images + epsilon * signed_grad
adv_images = tf.clip_by_value(adv_images, clip_value_min=0.0, clip_value_max=1.0)
return adv_images
2.训练数据中存在噪声
①交叉验证:是一种用于评估模型性能的技术,它可以将数据集分成多个子集,其中一个子集作为验证集,其余的子集作为训练集。然后可以用训练集来训练模型,用验证集来评估模型的性能。由于可以对数据集进行多次划分和训练,因此交叉验证可以在相对较小的数据集上进行模型评估和选择。
下面是一些使用交叉验证减轻噪声影响的方法:
-
留一交叉验证(Leave-One-Out Cross-Validation,简称 LOOCV):将每个数据点都单独作为验证集,其他数据点作为训练集。这种方法适用于小数据集,但是计算开销很大。
-
K 折交叉验证(K-Fold Cross-Validation):将数据集分成 K 个子集,其中一个子集作为验证集,其余 K-1 个子集作为训练集。这个过程重复 K 次,每次选择一个不同的验证集。K 折交叉验证通常是最常用的交叉验证方法。
-
带重复的 K 折交叉验证(Repeated K-Fold Cross-Validation):重复 K 折交叉验证多次,并且每次都进行不同的随机划分。这种方法适用于数据集较小或者需要进行比较准确的模型选择。
使用交叉验证可以减轻噪声的影响,但也需要注意一些问题,如过度拟合、训练时间等。交叉验证的具体实现可以使用相关库的交叉验证函数,如 Scikit-Learn 中的 cross_val_score
函数。
②集成学习(Bagging)
是一种将多个模型组合在一起以提高性能的方法。这些模型可以是相同的,也可以是不同的,常见的集成学习方法包括投票、平均值和堆叠等。
下面是一些使用集成学习减轻噪声影响的方法:
-
投票集成(Voting Ensemble):将多个模型的预测结果进行投票,选择获得最多票数的结果作为最终预测结果。这种方法适用于模型性能相似的情况下。
-
平均值集成(Averaging Ensemble):将多个模型的预测结果进行平均,得到最终预测结果。这种方法适用于模型性能相差较大的情况下。
-
堆叠集成(Stacking Ensemble):将多个模型的预测结果作为输入,训练一个元模型(Meta Model)来预测最终结果。这种方法适用于模型性能不同、模型类型不同的情况下。
使用集成学习可以减轻噪声的影响,但也需要注意一些问题,如模型间的相关性、计算开销等。集成学习的具体实现可以使用相关库的集成学习函数,如 Scikit-Learn 中的 VotingClassifier
、BaggingClassifier
、RandomForestClassifier
、StackingClassifier
等。
3.模型复杂度较高
①正则化项:是一种约束模型参数的方法,通过惩罚模型参数的大小来避免模型过度拟合训练数据。
常见的正则化项包括 L1 正则化项和 L2 正则化项。L1 正则化项将模型参数的绝对值作为惩罚项,而 L2 正则化项将模型参数的平方和作为惩罚项。在实践中,L2 正则化项常用于深度学习模型的训练中。
在深度学习模型中,可以使用 Keras 中的正则化项来实现 L2 正则化。具体来说,可以在模型定义的每个层中添加 kernel_regularizer=tf.keras.regularizers.l2(l=0.01)
参数来指定 L2 正则化项,其中 l
是正则化项的权重,可以根据具体情况进行调整。
以下是一个简单的使用 L2 正则化项的例子:
import tensorflow as tf
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu', input_shape=(784,), kernel_regularizer=tf.keras.regularizers.l2(l=0.01)),
tf.keras.layers.Dense(64, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(l=0.01)),
tf.keras.layers.Dense(10, activation='softmax')
])
在这个例子中,我们使用了两个带有 L2 正则化项的 Dense 层,并将权重值设为 0.01。这样就可以通过正则化项来限制模型参数的大小,从而防止过拟合。
需要注意的是,在使用正则化项时,需要仔细选择正则化权重的大小。如果权重过大,可能会导致模型欠拟合;如果权重过小,可能会无法有效地避免过拟合。通常需要通过交叉验证等方法来确定正则化项的最佳权重值。
②Dropout:
Dropout是深度学习中一种常用的正则化技术,用于减少神经网络的过拟合。其核心思想是在训练过程中,以一定的概率随机将神经网络中的某些神经元输出置为0,从而强制让网络在每次训练时都只能使用部分神经元进行前向传播和反向传播。
Dropout的主要作用是防止神经网络对某些特定的输入特征进行过拟合,使得网络对于其他的输入具有更好的泛化能力。Dropout不仅可以降低网络过拟合的风险,还可以提高网络的训练速度,使得网络的收敛速度更快,而且不需要进行复杂的参数调节。
import torch
import torch.nn as nn
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.dropout = nn.Dropout(p=0.5) # p为dropout概率
self.fc2 = nn.Linear(20, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # 在每次前向传播时应用dropout
x = torch.sigmoid(self.fc2(x))
return x
在这个例子中,我们定义了一个包含两个全连接层和一个Dropout层的神经网络。在每次前向传播时,Dropout层以0.5的概率随机将神经元输出置为0。这个网络可以用于二分类问题,最终的输出是一个介于0和1之间的数,表示属于第一类的概率。
Dropout 在训练和测试时有什么区别?为什么?
- 训练时,经过 Dropout 的输出值会乘以
;测试时不会。
- 经过 Dropout 后,输入
x
的期望输出将变为p*0 + (1-p)*x = (1-p)x
(p
的可能变为 0,1-p
的可能保持不变); - 为了还原未经过 Dropout 的期望值,故需要乘以
为什么 Dropout 能防止过拟合?
- 直观上,Dropout 会使部分神经元失活,减小了模型容量,从而降低了模型的拟合能力;
- 宏观上,Dropout 提供了一种廉价的 Bagging 集成方法(共享权重);
- 隐藏单元经过 Dropout 后,必须学习与不同采样的神经元合作,使得神经元具有更强的健壮性(减少神经元之间复杂的共适应关系);
③Early Stoping:
Early stopping是一种在训练神经网络时防止过拟合的方法,它通过在训练过程中监控验证集的表现,一旦验证集上的性能不再提高,就停止训练,避免了模型对训练集的过度拟合。具体来说,当验证集上的性能在连续若干次迭代中没有提高时,我们就认为模型已经过拟合了,此时就停止训练。
在实现上,我们需要设置一个patience参数来控制连续多少次没有提高才停止训练,同时需要保存最好的模型权重。以下是一个简单的实现示例:
from keras.callbacks import EarlyStopping, ModelCheckpoint
# 定义EarlyStopping和ModelCheckpoint回调函数
early_stopping = EarlyStopping(monitor='val_loss', patience=10)
model_checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, save_weights_only=True)
# 训练模型
model.fit(X_train, y_train, epochs=100, batch_size=32, validation_data=(X_val, y_val), callbacks=[early_stopping, model_checkpoint])
④降低模型复杂度
-
减少模型参数:可以通过减少网络层数、降低每层神经元数等方式来减少模型参数。这样可以使得模型更加简单,避免过拟合。
-
使用正则化方法:正则化方法可以通过增加正则项的方式,限制模型的复杂度。例如,L1正则化、L2正则化、dropout等方法都可以有效地降低模型复杂度。
-
使用简单的模型:有时候,使用简单的模型比使用复杂的模型更加有效。例如,对于某些问题,线性模型可能比深度神经网络更加适合。
-
使用集成学习:集成学习可以通过将多个不同的模型组合起来,来降低模型复杂度。例如,可以通过bagging、boosting等方法来实现集成学习。