机器学习基础

  • 机器学习的四个分支
  • 1 监督学习 supervised learning
  • 2 无监督学习
  • 3 自监督学习
  • 4 强化学习
  • 评估机器学习模型
  • 训练集、验证集和测试集
  • 评估模型的注意事项
  • 数据预处理、特征工程和特征学习
  • 神经网络的数据预处理
  • 特征工程
  • 过拟合与欠拟合
  • 减小网络大小
  • 添加权重正则化
  • 添加dropout 正则化
  • 机器学习的通用工作流程


机器学习的四个分支

1 监督学习 supervised learning

  • 概念: 给定一组样本(通常由人工标注),它可以学会将输入数据映射到已知目标(也叫标注annotation
  • 应用: 主要包括分类回归,还有很多变体:
    序列生成(sequence generation):给定一张图像,预测描述图像的文字。
    语法树预测(syntax tree prediction):给定一个句子,预测其分解生成的语法树。
    目标检测(object detection):给定一张图像,在图中特定目标周围画一个边界框。
    图像分割(image segmentation):给定一张图像,在特定物体上画一个像素级的掩码(mask)。

2 无监督学习

  • 概念: 是指在没有目标的情况下寻找输入数据的有趣变换,其目的在于数据可视化数据压缩数据去噪或更好的理解数据中的相关性
  • 应用: 这是数据分析的必备技能,在解决监督学习问题之前,为了更好地了解数据,它通常是一个必要的步骤。降维(dimensionality reduction)聚类(clustering) 就是无监督学习方法。

3 自监督学习

  • 概念: 自监督学习是监督学习的一个特例!没有人工标注的标签的监督学习,标签仍然存在(因为总要有什么东西来监督学习过程),但它们是从输入数据中生成的,通常是使用启发式算法生成的。
  • 应用: 自编码器(autoencoder)、时序监督学习(temporally supervised learning)

4 强化学习

  • 概念: 在强化学习中,智能体(agent)接收有关其环境的信息,并学会选择使某种奖励最大化的行动。
  • 应用: 目前主要集中在研究领域,主要在游戏方面取得实践重大成功。

评估机器学习模型

  • 机器学习的目的是得到可以泛化(generalize)的模型,即在前所未见的数据上表现很好的模型,而过拟合则是核心难点。
  • 评估机器学习模型也就是衡量模型的泛化能力。

训练集、验证集和测试集

  • 为什么需要验证集,而不是只需要训练集和测试集:原因在于开发模型的时候总是需要调节模型配置,比如模型的超参(hyperparameter),这个调节过程需要模型在验证数据上的性能作为反馈信号。
  • 信息泄露: 每次基于模型在验证集上的性能来调节模型超参数,都会有一些关于验证数据的信息泄露到模型中。
  • 测试集: 你的模型一定不能读取有关测试集的任何信息,如果基于测试集性能来调节模型,那么对泛化能力的衡量是不准确的。
  • 如果可用数据很少,还有几种高级方法,三种经典的评估方法
    (1)简单的留出验证: 留出一定比例的测试数据,在剩下的数据上训练模型,但是由于不能在测试集上调节模型,所以要留一个验证集,如图:

    相关代码:
num_validation_samples = 10000
np.random.shuffle(data) #通常需要打乱数据
validation_data = data[:num_validation_samples] #定义验证集
data = data[num_validation_samples:]
training_data = data[:] #定义训练集

#在训练数据上训练模型,并在验证数据上评估模型
model = get_model()
model.train(training_data)
validation_score = model.evaluate(validation_data)

#现在可以调节模型、重新训练、评估、然后再调节
model = get_model()
model.train(np.concatenate([training_data,validation_data]))
test_score = model.evaluate(test_data)

该方法的缺点:如果数据很少,测试部分包括的样本就很少,那么评估的结果就会有偏差。

(2)K折验证: 将数据划分,为大小相同的K个分区。对于每个分区i,在剩余K-1个分区上训练模型,然后在分区i上评估模型。最终分数等于等于K个分数的平均值。对于不同的训练集-测试集划分,如果模型性能变化很大,那么这种方法很有用。如图:

deep learning pytorch中文 deep learning with python 2_正则化


相关代码:

k = 4
num_validation_samples = len(data) // k
 
np.random.shuffle(data) #打乱数据

validation_scores = []
for fold in range(k):
    validation_data = data[num_validation_samples*fold:num_validation_samples*(fold+1)] #选择验证数据分区
    training_data = data[:num_validation_samples*fold]+data[num_validation_samples*(fold+1):] #使用剩余数据作为训练数据,+运算符是列表合并
    #创建一个全新的模型实例(未训练)
    model = get_model()
    model.train(training_data)
    validation_score = model.evaluate(validation_data)
    validation_scores.append(validation_score)
 
validation_score = np.average(validation_scores) #最终验证分数,k折验证分数的平均值
 
#在所有非测试数据上训练最终模型
model = get_model()
model.train(data)
test_score = model.evaluate(test_data)

(3)带有打乱数据的重复K折验证: 这种方法是多次使用K折验证,在每次将数据划分为K个分区之前都先将数据打乱。最终分数是每次K折验证分数的平均值。这种方法对于数据相对较少,又想尽可能精确的评估模型来说很有效,但是缺点在于,它的计算代价很高,因为一共要训练和评估P x K个模型(P是重复次数)。

评估模型的注意事项

  • 1.数据代表性:划分数据集时随机打乱数据。
  • 2.时间箭头:如果想根据过去预测未来,不能随机打乱数据,要确保测试数据的时间晚于训练数据。
  • 3.数据冗余:如果数据中有多次重复的数据点,不能随机打乱数据,要确保训练集和验证集没有交集。

数据预处理、特征工程和特征学习

神经网络的数据预处理

  • 数据预处理目的: 使原始数据更适用于神经网络处理,包括向量化、标准化、处理缺失值和特征提取
  • 预处理方法:
    1.向量化: 无论处理什么数据(声音、图像还是文本),都必须首先将其转换为张量,这就是数据向量化(data vectorization)。
    2.值标准化: 一般来说,将取值较大的数据(比如多位整数,比网络权重的初始值大很多)或异质数据,输入神经网络中是不安全的。这么做可能导致较大的梯度更新,进而导致网络无法收敛。因此输入数据应当具有以下特征:
    [取值较小]:大部分在0~1
    [同质性]:所有特征的取值都应该在大致相同的范围
    [更严格的标准化]:将每个特征分别标准化,使其平均值为0,标准差为1;x -= x.mean(axis = 0) x /= x.std(axis=0)
    3.处理缺失值: 一般来说,对于神经网络,将缺失值设置为0是完全的,只有0不是一个有意义的值。网络能够从数据中学到0意味着缺失数据,并且会忽略这个值。注意:如果测试数据中可能有缺失值,而网络是在没有缺失值的数据上训练,那么网络不可能学会忽略缺失值,在这种情况下,应该人为生成一些有缺失项的训练样本:多次复制一些训练样本,然后删除测试数据中可能缺失的某些特征。

特征工程

  • 概念: 特征工程是指将数据输入模型之前,利用你自己关于数据和机器学习算法的知识对数据进行硬编码的变换,以改善模型的效果。多数情况下,一个机器学习模型无法从完全任意的数据中进行学习,呈现给模型的数据应该便于模型进行学习。
  • 解释:
    (1) 特征工程简单讲就是发现对因变量y有明显影响作用的特征,通常称自变量x为特征,特征工程的目的是发现重要特征。
    (2)特征工程的目的是筛选出更好的特征,获取更好的训练数据。因为好的特征具有更强的灵活性,可以用简单的模型做训练,更可以得到优秀的结果。“工欲善其事,必先利其器”,特征工程可以理解为利其器的过程。互联网公司里大部分复杂的模型都是极少数的数据科学家在做,大多数工程师们做的事情基本是在数据仓库里搬砖,不断地数据清洗,再一个是分析业务不断地找特征。 例如,某广告部门的数据挖掘工程师,2周内可以完成一次特征迭代,一个月左右可以完成模型的小优化,来提升auc。
  • 特征工程的本质: 用更简单的方式表述问题,从而使问题变得更容易。它通常需要深入理解问题。
  • 特征工程的重要性: 良好的特征工程仍然可以让你用更少的资源更优雅地解决问题。良好的特征可以让你用更好的数据解决问题。深度学习模型自主学习特征的能力依赖于大量的训练数据,如果只有很少的样本,那么特征的信息价值就变得非常重要。

过拟合与欠拟合

  • 机器学习的根本问题是优化和泛化之间的对立。
  • 为了防止模型从训练数据中学到错误或无关紧要的模式,最优解决方法是获取更多的训练数据。模型的训练数据越多,泛化能力自然也越好。
  • 次优的解决方法是调节模型允许存储的信息量,或对模型允许存储的信息加以约束。这种降低过拟合的方法叫做正则化(regularization),常见的正则化方法如下。

减小网络大小

  • 深度学习模型通常都很擅长拟合训练数据,但真正的挑战在于泛化,而不是拟合。
  • 因此,为了让损失最小化,网络必须学会对目标具有很强预测能力的压缩表示,这也正是我们感兴趣的数据表示。同时请记住,你是用的模型应该具有足够多的参数,以防止欠拟合,即模型应该避免记忆资源不足。在容量过大与容量不足之间要找到一个折中
  • 当然没有魔法公式能确定最佳层数或每层的最佳大小。我们只能去寻找合适的模型大小,一般是开始选择相对较少的层和参数,然后逐渐增加层的大小或增加新层,直到这种增加对验证损失的影响变得很小。

添加权重正则化

  • 奥卡姆剃刀原理(Occam’s razor):如果一件事情有两种解释,那么最可能正确的解释就是最简单的那个,即假设更少的那个。
  • 这个原理也适用于神经网络学到的模型: 给定一些训练数据和一种网络结构,很多组权重值(即很多模型)都可以解释这些数据。简单模型比复杂模型更不容易过拟合!
  • 这里的简单模型:指参数值分布的熵跟小的模型(或参数更少的模型)。
  • 权重正则化(weight regularization):,这强制让模型权重只能取较小的值,从而限制模型的复杂度,这使得权重值的分布更加规则(regular)。其实现方法是向网络损失函数中添加与较大权重值相关的成本(cost),这个成本有两种形式:
    (1)L1正则化:添加的成本与权重系数的绝对值【权重的L1范数】成正比。
    (2)L2正则化:添加的成本与权重系数的平方【权重的L2范数】成正比。也叫权重衰减!
  • 在keras中,添加权重正则化的方法是向层传递权重正则化项实例作为关键字参数。
from keras import regularizers

model = models.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001), activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001), activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

l2(0.001)的意思是该层权重矩阵的每个系数都会使网络总损失增加 0.001 * weight_coefficient_value。注意,由于这个惩罚项只在训练时添加,所以这个网络的训练损失会比测试损失大很多。
当然,你还可以使用:

regularizers.l1(0.001) #l1正则化
regularizers.l1_l2(l1=0.001, l2=0.001) #同时做l1和l2正则化

添加dropout 正则化

  • dropout 是神经网络中最有效也最常用的正则化方法之一,由多伦多大学的Geoffrey Hinton和他的学生开发。
  • 对某层使用 dropout 就是在训练过程中随机将该层的一些输出特征舍弃(设置为0)。
  • dropout比率:是被设置为 0 的特征所占的比例,通常在0.2~0.5范围内。测试时没有单元被舍弃,而该层的输出值需要按dropout比率缩小,因为这时比训练时有更多的单元被激活,需要加以平衡。
  • 例: 假设矩阵layer_output, 其 形 状 为 (batch_size, features)。训练时,我们随机将矩阵中一部分值设为 0。layer_output *= np.random.randint(0, high=2, size=layer_output.shape) # 训练时,舍弃50%的输出单元 测试时,我们将输出按 dropout 比率缩小。这里我们乘以 0.5(因为前面舍弃了一半的单元)。layer_output *= 0.5 # 测试时 这样做麻烦,可以将两个运算都在训练时进行,测试时输出保持不变。这也是实践中的方式!
    layer_output *= np.random.randint(0, high=2, size=layer_output.shape) # 训练时 layer_output /= 0.5 # 注意,是成比例放大而不是成比例缩小
  • Hinton dropout的灵感:来自于银行的防欺诈机制。“我去银行办理业务。柜员不停的换人,于是我问其中的一个人这是为什么。他说他不知道,但他们经常换来换去的。我猜想,银行工作人员想要成功欺诈银行,他们之间要互相合作才行,这让我意识到,在每个样本中随机删除不同的部分神经元,可以阻止他们的阴谋,因此可以降低过拟合”。其核心思想是在层的输出值中引入噪声,打破不显著的偶然模式。如果没有噪声的话,网络将会记住这些偶然模式。
  • 在keras中,可以通过Dropout层向网络中引入dropout,dropout将被应用于前面一层的输出
# 向 IMDB 网络中添加 dropout
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))
  • input 的 dropout 概率推荐是0.8(添加噪声);hidden layer 推荐是0.5(随机生成的网络结构最多)
  • dropout缺点: 缺点就是会明显增加训练时间,因为引入dropout之后相当于每次只是训练的原先网络的一个子网络,为了达到同样的精度需要的训练次数会增多。dropout的缺点就在于训练时间是没有dropout网络的2-3倍。

机器学习的通用工作流程

  • 1 定义问题,收集数据集: 机器学习只能记住训练数据中存在的模式。你只能认识你已经看到过的东西。利用机器学习对过去的数据进行训练,用于预测未来,这样的做法假设未来的行为将于过去类似。但是,通常并非如此。
  • 2 选择衡量成功的指标: 要控制某些东西,你需要能够观察到它。为了取得成功,你必须定义成功是什么,是正确率?精度或者召回率?还是客户保留率?你的成功指标的定义将会指导你选择损失函数,损失函数就是你模型将要优化的内容。损失函数应该能够直接与你的目标保持一致,例如你业务的成功。
    对于均衡分类问题,这类问题中每个类别都有相同的可能性,准确率和ROC AUC是常用的指标。
    对于类不平衡问题,你可以用精度和召回。
    对于排名问题或者多标签问题,你可以用平均精度。
    定义你自己的评价指标并不罕见。要了解机器学习成功指标的多样性以及它们是如何关系不同的问题域,有必要去了解Kaggle上的数据科学竞赛,这些竞赛展示了广泛的问题和评价指标。
  • 3 确定评估方法:
    保留一个hold-out验证集,当你有足够多的数据时,用这种方法
    K-fold 交叉验证。数据太少,不足以使用第一种验证方法的使用,用这种方法。
    迭代 K-fold 交叉验证。只有很少的数据可用时,用于执行高度准确的模型评估。
    选择其中一个。在大多数情况下,第一种方法工作得很好。
  • 4 开发比基准更好的模型:
    在这个阶段,你的目标是做到统计功效 (statistical power),也就是开发一个能够击败基准的模型。在MNIST数字分类示例中,任何达到大于0.1精度都可以说是具有statistical power; 在IMDB的例子中,大于0.5就可以了。
    (1)请注意,达到statistical power并不总是可能的。如果在尝试了多个合理的体系构架之后,仍然无法打败一个随机基准,那么可能是你要求的问题的答案无法从输入数据中获得。记住你提出的两个假设:
    你假设你可以根据给定的输入预测输出
    你假设你的可用数据有足够的信息用于学习输入与输出之间的关系
    这些假设有可能是错误的,在这种情况下你必须重新开始。
    (2)假设目前为止一切都很顺利,你需要作出三个关键的选择来建立你的第一个工作模型:
    最后一层的激活函数 。这为网络的输入设定了限制。例如,在IMDB分类问题中,最后一层使用了sigmoid; 在回归问题中,最后一层没有使用任何激活函数
    损失函数。这应该与你正在尝试解决的问题的类型相匹配。例如在IMBD二元分类问题中,使用了binary_crossentropy,回归问题中使用了mse等等。
    优化配置。你将使用什么优化器?学习率是多少?在大多数情况在,使用rmsprop和默认的学习率是安全的。
    (3)关于损失函数的选择 ,请注意,直接优化衡量问题成功的指标不一定总是可行的。有时候,难以将指标转换为损失函数;损失函数毕竟只需要一个小批量的数据就能计算(理想情况下,损失函数只需要一个数据就能计算),并且损失函数必须是可微分的(否则,你不能使用反向传播来训练你的网络)。例如,广泛使用的分类度量 ROC AUC 就不能直接优化。因此,在分类问题中,通常针对ROC AUC 的替代指标(例如,交叉熵)进行优化,一般来说,你希望如果越低的交叉熵,你就能获得更高的ROC AUC。
  • 5 扩大模型的规模: 开发过拟合的模型
    (1)一旦你的模型达到了statistical power,那么问题就变成了:你的模型是否足够强大?你是否有足够多的网络层和参数来正确建模你的问题?例如,具有两个神经元的单层网络在MNIST具有statistical power,但是不能很好的解决MNIST分类问题。
    请记住,机器学习中最困难的就是在优化和泛华之间取得平衡;理想的模型就是站在欠拟合与过拟合之间。要弄清楚这个边界在哪里,你必须先穿过它。
    (2)要弄清楚你需要多大的模型,你必须先开发一个过拟合的模型。这很容易:
    增加网络层
    让网络层变大
    训练更多次
    (3)始终监视着训练误差和验证误差,以及你所关心的metrics。当你看到模型在验证集上性能开始下降,就达到了过拟合。下个阶段是开始正则化和调整模型,尽可能的接近既不是欠拟合又不是过拟合的理想模型。
  • 6 模型正则化与调节超参数:
    这一步将花费大量时间,你将重复修改你的模型,并对其进行训练,在验证集上进行评估,再次修改,如此重复,知道模型达到所能达到的最佳效果。以下是你应该尝试做的一些事情:
    添加Dropout
    尝试不同的体系结构的网络:添加或者删除网络层
    添加 L1/L2 正则化
    尝试不同的超参数(例如每一层的神经元个数或者优化器学习率),以获得最佳的参数选择
    (可选)迭代特征工程:添加新特征,或者删除似乎没有提供信息的特征
    请注意以下几点:每次使用验证集来调整模型参数时,都会将有关验证的信息泄露在模型中。重复几次是无害的;但是如果重复了很多很多次,那么最终会导致你的模型在验证集上过拟合(即使没有直接在验证集上进行训练),这使得验证过程不太可靠。
    一旦你开发出令人满意的模型,你可以根据所有可用的数据(训练集和验证集)来训练你最终的模型。如果测试集的结果明显低于验证集上结果,那么可能意味着你的验证过程不太可靠,或者你的模型在验证集中已经过拟合了。在这种情况下,你可能需要更为靠谱的验证策略(例如迭代K-fold验证)