更快的 RCNN 对象检测
简介
更快的 RCNN 是由罗斯·吉斯克、邵青·任、明凯·何、孙健于 2015 年提出的一种物体检测架构,是像 YOLO(你只看一次)和 SSD(单次检测器)一样使用卷积神经网络的著名物体检测架构之一。
让我们解释一下这个架构是如何工作的,
更快的 RCNN 由 3 部分组成
- 第 1 部分:卷积层
在这一层中,我们训练过滤器来提取图像的适当特征,例如,假设我们要训练这些过滤器来提取人脸的适当特征,那么这些过滤器将通过训练只存在于人脸中的形状和颜色来学习。
所以我们可以把卷积层比作咖啡过滤器,咖啡过滤器不让咖啡粉通过杯子,所以我们的卷积层学习物体的特征,不让任何东西通过,只让想要的物体通过。
- 咖啡粉+咖啡液=输入图像
- 咖啡过滤器= CNN 过滤器
- 咖啡液体 CNN 的最后一个专题图
让我们更多地讨论卷积神经网络,
卷积网络通常由卷积层、汇集层和最后一个组件组成,该组件是完全连接的或另一个扩展的组件,将用于适当的任务,如分类或检测。
我们通过沿着我们的输入图像的滑动滤波器来计算卷积,并且结果是称为特征图的二维矩阵。
池包括通过消除低值像素来减少要素地图中的要素数量。
最后一件事是使用全连接层来分类这些特征,这在快速 RCNN 中不是我们的情况。
- 第 2 部分:区域提议网络(RPN)
RPN 是一种小型神经网络,它在卷积层的最后一个特征图上滑动,预测是否存在对象,并预测这些对象的包围盒。
- 第 3 部分:类别和包围盒预测
现在,我们使用另一个完全连接的神经网络,该网络将 RPN 提出的区域作为输入,并预测对象类别(分类)和边界框(回归)。
- 培训
为了训练这种架构,我们使用 SGD 来优化卷积层过滤器、RPN 权重和最后完全连接的层权重。
应用更快的 RCNN
您可以使用 Tensorflow 对象检测 API ( 链接)在您的自定义数据集上训练更快的 RCNN 预训练模型(链接)。
高效 CNN 的快速培训
最近,已经证明深度卷积在设计高效网络方面非常有效,例如 MobileNet 和 ShuffleNet 。然而,这种高效的网络需要花费更长的时间来训练,通常需要 300-400 个历元,才能在 ImageNet 数据集上达到最先进的精度。
在本文中,我们描述了 在 ESPNetv2 论文(CVPR’19)中介绍的一种有效的学习率调度器 ,它允许在大约 100 个时期内训练有效的网络,而不会对准确性产生任何重大影响。为了展示 ESPNetv2 中学习率调度程序的卓越性能,我们在本文中使用了 ShuffleNetv2 的架构。我们注意到我们的发现与其他高效网络一致,包括 MobileNetv2 。
学习率调度程序
ESPNetv2 引入了余弦学习率的变体,其中学习率线性衰减直到周期长度,然后重新开始。在每个时期 t ,学习率ηₜ被计算为:
Figure 1: Cyclic learning rate policy with linear learning rate decay and warm restarts
F 紫苑培训
使用上述学习速率调度程序,我们在四个 TitanX GPUs 上使用 SGD 在两种不同的 FLOP 设置下为训练 ShuffleNetv2 总共 120 个历元,批量大小为 512:(1)41m flops 和(2) 146 MFLOPs。对于前 60 个时期,我们将 ηₘᵢₙ 、 ηₘₐₓ 和 T 分别设置为 0.1、0.5 和 5。对于剩余的 60 个纪元,我们设置 ηₘₐₓ=0.1 、 T=60 、 ηₘᵢₙ = ηₘₐₓ/T 。下图显示了 120 个时期的学习策略。
Figure 2: Cyclic learning rate scheduler with warm restarts for faster training proposed in the ESPNetv2 paper.
ImageNet 数据集上的结果
结果见图 3 和表 1。我们可以清楚地看到,学习率调度器(如上所述)能够实现更快的训练,同时提供与 ShuffleNet 论文中的线性学习率调度器相似的性能。
Figure 3: Epoch-wise training (left) and validation (right) accuracy of ShuffleNetv2 (41 MFLOPs) using linear (see ShuffleNetv1/v2 papers) and cyclic (discussed above) learning rate schedule.
Table 1: Top-1 accuracy of the ShuffleNetv2 model with two different learning rate schedules: (1) Linear, as in the ShuffleNet paper and (2) Cyclic, as in the ESPNetv2 paper (described above). The slight difference in reported and our implementation is likely due to the batch size. We used a batch size of 512 while in ShuffleNetv2 paper a batch size of 1024 is used.
我们可以在不同的工作上使用 ESPNetv2 排程器吗?
是的,调度程序是通用的,可以跨不同的任务使用。在 ESPNetv2 论文中,我们将它用于所有三个标准视觉任务:(1)图像分类,(2)对象检测,以及(3)语义分割。详见论文。
在 R 中安装和加载库的最快方法
在与一位同学合作之后,很明显我需要一种新的方式来加载我在学校学到的库。不是每个人都安装了相同的库,这可能会出错。我希望代码能为每个人无缝运行。此外,不得不反复编写相同的函数来安装和加载不同的库是很痛苦的。我和我的同学努力寻找一种简单的方法来做到这一点。然后我使用了一个名为 tictoc 的包来测量不同方法的速度。
注意:测试是在干净的全球环境中进行的。所有的包都已经被安装和加载了,所以测试可以保持一致(包会被重新加载)。
首先,为了进行分析,需要安装和加载 tictoc 包。我还将为要加载的包列表定义一个变量。
install.packages("tictoc")
library(tictoc)packages <- c("tidyverse", "dplyr", "stringr", "zoo", "ROCR", "caret", "class", "gmodels", "randomForest")
方法 1 —分别加载和安装每个库:
我注释掉了 install.packages,因为不管这个包是否存在,它都会重新安装。这不是最有效的方法,因为如果省略 install.packages 并且该软件包在当前安装中不存在,您将重新安装所有内容或得到一个错误。
总时间:0.11 秒
tic("basic")#install.packages(packages)
library(tidyverse)
library(dplyr)
library(stringr)
library(zoo)
library(ROCR)
library(caret)
library(class)
library(gmodels)
library(randomForest)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)
方法 2——使用 for 循环
总时间:0.13 秒
tic("for loop")libraries <- function(packages){
for(package in packages){
#checks if package is installed
if(!require(package, character.only = TRUE)){
#If package does not exist, then it will install
install.packages(package, dependencies = TRUE)
#Loads package
library(package, character.only = TRUE)
}
}
}libraries(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)
方法 3 —使用应用方法
总时间:0.07 秒
tic("apply")ipak <- function(pkg){
new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
if (length(new.pkg))
install.packages(new.pkg, dependencies = TRUE)
sapply(pkg, require, character.only = TRUE)
}ipak(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)
方法 4 —使用install . load包
在这篇文章中,我假设用户已经安装了 install.load 来计算时间,因为这只会发生一次。要安装的代码只是出于可复制性的目的。
总时间:0.12 秒
tic("install.load")if (!require("install.load")) install.packages("install.load")
library(install.load)install_load(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)
方法 5——使用 吃豆人 包
这也是在假设用户已经安装了 pacman 的情况下计算的。
总时间:0.06 秒
tic("pacman")if (!require("pacman")) install.packages("pacman")
#pacman will not accept a character vector so the same packages are repeated
pacman::p_load("tidyverse", "dplyr", "stringr", "zoo", "ROCR", "caret", "class", "gmodels", "randomForest")toc(log = TRUE)
log.txt <- tic.log(format = TRUE)
最终判决
安装和/或加载许多软件包的最快方法是使用 pacman 软件包。但是,如果您不想安装额外的软件包,最好使用 apply 方法。它只是稍微慢了一点。
值得一提的是,对于“基本”测试,如果用户不确定他们当前已经安装的软件包,由于上面提到的原因,这种方法会变得非常慢。
快速文本分类器是如何工作的?
当线性模型优于复杂的深度学习模型时
由脸书研究院开发的 FastText ,是一个高效学习单词表示和文本分类的库。FastText 支持单词和句子的监督(分类)和非监督(嵌入)表示。
然而,FastText 包的文档没有提供关于实现的分类器和处理步骤的细节。
在这里,我们试图跟踪 FastText 包的底层算法实现。
简单地说,没有什么魔法,但是很少有聪明的步骤:
- 通过平均单词/n-gram 嵌入来获得句子/文档向量。
- 对于分类任务,使用多项式逻辑回归,其中句子/文档向量对应于特征。
- 当在具有大量类的问题上应用 FastText 时,可以使用分层的 softmax 来加速计算。
【fast Text】高效文本分类锦囊妙计 :
论文:http://aclweb.org/anthology/E17-2068
- 从平均为文本表示的单词表示开始,并将它们馈送到线性分类器(多项式逻辑回归)。
- 作为隐藏状态的文本表示,可在要素和类之间共享。
- Softmax 层,以获得预定义类别的概率分布。
- Hierarchial Softmax:基于霍夫曼编码树,用于降低计算复杂度 O(kh) 到 O(hlog(k)),其中 k 是类的数量,h 是文本表示的维数。
- 使用一袋 n-grams 来保持效率而不损失准确性。没有明确使用词序。
- 使用哈希技巧来保持 n 元语法的快速和内存高效映射。
- 它是用 C++编写的,在训练时支持多重处理。
FastText vs 深度学习进行文本分类:
对于不同的文本分类任务,FastText 显示的结果在准确性方面与深度学习模型不相上下,尽管在性能上快了一个数量级。
注:简单的 FastText 算法的高精度表明,文本分类问题仍然没有被很好地理解,不足以构建真正有效的非线性分类模型。【3】
参考文献:
快速文本表示和分类库。-Facebook 研究/快速文本
github.com](https://github.com/facebookresearch/fastText) [## 快速文本:引擎盖下
在这里我们将看到一个性能最好的嵌入库是如何实现的。
towardsdatascience.com](/fasttext-under-the-hood-11efc57b2b3)
https://pdfs . semantic scholar . org/9d 69/93f 60539d 30 ee 325138 b 3465 aa 020 fa 3 BCB 4 . pdf
https://gist . github . com/shagunsodhani/432746 f 15889 F7 F4 a 798 BF 7 f 9 EC 4 b 7d 8
推特快速文本情感分析:简单指南
快速文本架构、清理、上采样和推文情感的本质
A robot learning sentiments
在这篇文章中,我们介绍了 fastText 库,它如何在文本分类方面比一些深度神经网络实现更快的速度和类似的准确性。接下来,我们将展示如何借助由AWS comprehende生成的数据来训练情感分析模型。在的另一篇文章中,我们展示了如何使用 AWS Elastic Beanstalk 创建一个机器学习服务器来服务你的模型。
fast text——浅层神经网络架构
FastText 是由 facebook AI 开发的开源 NLP 库,最初于 2016 年发布。它的目标是高效地提供单词嵌入和文本分类。据他们的作者称,在准确性方面,它通常与深度学习分类器不相上下,并且在训练和评估方面快许多数量级。[1]
这使得 fastText 成为构建 NLP 模型并为生产环境生成实时预测的优秀工具。
FastText 架构概述
FastText 的核心依赖于用于单词表示的连续单词包 (CBOW)模型和一个分层分类器来加速训练。
连续单词包(CBOW)是一个浅层神经网络,它被训练来从其邻居预测一个单词。FastText 用预测类别取代了预测单词的目标。这些单层模型训练速度非常快,并且可以很好地扩展。
此外,fastText 用分层的 softmax 替换标签上的 softmax。这里每个节点代表一个标签。这减少了计算量,因为我们不需要计算所有标签的概率。有限数量的参数减少了训练时间。
fastText hierarchical architecture for sentiment analysis.
训练速度更快,但结果相似
根据最初的论文[1],fastText 实现了与其他算法类似的结果,同时训练速度快得多。
正如您在下面看到的,fastText 的训练时间在 1 到 10 秒之间,而其他型号的训练时间为几分钟或几小时。
Bag of Tricks for Efficient Text Classification — Joulin 2016
打开用于情感分析的数据集
大多数用于文本分类的开放数据集都很小,我们注意到,除了英语之外,很少(如果有的话)可以用于其他语言。因此,除了为情感分析提供指导,我们还想为情感分析提供开放数据集【2】。
出于这些原因我们提供了包含推文列表及其观点的文件:
- 英语推文数据集=>630 万条推文可用。
- 西班牙语推文数据集=>120 万推文。
- 法语推文数据集= > 25 万条推文
- 意大利推文数据集= > 425 000 条推文
- 德国推文数据集= > 210 000 条推文
这些都是得益于 AWS 领悟 API 生成的。对于西班牙语和法语,首先使用谷歌翻译将推文翻译成英语,然后使用 AWS 领悟进行分析。情绪分为积极、消极、中性或混合。
对于本文,我们使用的是英语推文数据集。
为情绪清理推文。
他们说清理通常是数据科学家 80%的时间。可悲的是,这里没有例外。为了获得最好的结果,我们必须确保数据接近正确的英语,因为我们在处理推文,这不是一件容易的事情。
Example of (funny) misspelled tweets — source: thepoke.co.uk
我们的目标是清理推文,使它们更容易被机器阅读。有许多技术可以清理文本。最著名的是词汇化、词干化和停用词。
- 词干化和词条化的目标都是将一个单词的屈折形式和派生相关形式简化为一个通用的基本形式。例如:am,are,is = > be / dog,dogs,dogs’s,dogs =>dog。)这些减少了语料库的大小及其复杂性,允许更简单的单词嵌入(am、are 和 is 共享相同的精确单词向量)。
- 停用词过滤增加噪音或对机器理解文本没有价值的常用词。例子:a,and,the…
虽然词干化和词汇化有助于情感分析,但停用词过滤并不简单。停用词的目标是删除不必要的词,但是如果您查看可用的停用词列表,例如来自 NLTK 库的列表,您会发现可能传达负面情绪的词,例如:not,don 't,has not…但是对于情感分析问题,我们希望保留负面词汇。很明显,“这是一个好游戏”和“这不是一个好游戏”提供了相反的观点。因此,用户需要编辑停用词列表以排除传达负面含义的词,或者根本不使用停用词。我们选择了后者。
此外, tweets 是包含大量表情符号、缩写、标签、拼写错误的单词和俚语的短信。其中大多数对情感分析没有什么价值,需要清理:
- 缩写/俚语清理。如果我们想简化我们的问题,当有合适的替代方法时,我们需要去掉缩写并翻译俚语。然而,很难找到这样的词汇库或数据库。为此,我们必须创建一个列表。去我的 GitHub 页面看看吧。
#CONTRACTIONS is a list of contractions and slang and their conversion. { "you've":"you have", "luv":"love", etc...}
tweet = tweet.replace("’","'")
words = tweet.split()
reformed = [CONTRACTIONS[word] if word in CONTRACTIONS else word for word in words]
tweet = " ".join(reformed)
- **修复拼错的单词。**这里我们使用一个正则表达式,使用 regex 来删除单词中的重复字符。除了 regex 之外,您还可以使用其他能够检测和修复拼写错误的库。遗憾的是,它们非常慢,当你每天有数千条推文要分析时,这在生产中是不可接受的。
import itertools
tweet = ''.join(''.join(s)[:2] for _, s in itertools.groupby(tweet))
- 转义 HTML 字符:Twitter API 有时会返回 HTML 字符。当这种情况发生时,我们需要将它们转换成 ASCII 形式。比如, %20 转换成空格,&;被转换为&。为此,我们使用 Beautiful Soup,一个用于解析 HTML 和 XML 文档的 Python 包。
from bs4 import BeautifulSoup
tweet = BeautifulSoup(tweet).get_text()
- 移除标签/账户:需要移除使用 Twitter 标签(#)和账户(@)的名称。我们不希望一个足球运动员的名字被我们的模型永远归类为“负面”,仅仅因为他在我们的数据集中与糟糕的评论相关联。
tweet = ' '.join(re.sub("(@[A-Za-z0-9]+)|(#[A-Za-z0-9]+)", " ", tweet).split())
- 删除网址:
tweet = ' '.join(re.sub("(\w+:\/\/\S+)", " ", tweet).split())
- 去除标点符号:标点符号不用于“单词袋”技术。
tweet = ' '.join(re.sub("[\.\,\!\?\:\;\-\=]", " ", tweet).split())
- 小写:将所有内容转换为小写,以避免区分大小写的问题:
#Lower case
tweet = tweet.lower()
- 表情符号/表情符号:在一条推文中,表情符号和表情符号用“\”或标点符号表示,因此没有正确标记。为了保持它们的意义,我们需要将它们转换成更简单的形式。对于表情符号,有一个 **python 库“e moji”**通过将表情符号代码转换为标签来实现这一点。对于表情符号:-),您必须提供您的列表,我们会在 GitHub 页面上提供。
#Part for smileys - SMILEY is a list of smiley and their conversion. {"<3" : "love", ":-)" : "smiley", etc...}
words = tweet.split()
reformed = [SMILEY[word] if word in SMILEY else word for word in words]
tweet = " ".join(reformed)
#Part for emojis
tweet = emoji.demojize(tweet)
- 口音只限于英语,但广泛用于其他语言,口音经常被放错地方或遗忘。对付它们最简单的方法就是摆脱它们。
def strip_accents(text):
if 'ø' in text or 'Ø' in text:
#Do nothing when finding ø
return text
text = text.encode('ascii', 'ignore')
text = text.decode("utf-8")
return str(text)
要查看所有相关内容,请查看我的 GitHub 页面上的代码[2]。
格式化数据
FastText 需要标记数据来训练监督分类器。标签必须以前缀__label__
开头,这是它识别标签或单词的方式。下面是标签为正面和负面的推文所需格式的示例。
__label__POSITIVE congratulations you played very well yesterday.
__label__NEGATIVE disappointing result today.
...
我们使用下面的代码来格式化数据。
def transform_instance(row):
cur_row = []
#Prefix the index-ed label with __label__
label = "__label__" + row[0]
cur_row.append(label)
#Clean tweet and tokenize it
cur_row.extend( nltk.word_tokenize(tweet_cleaning_for_sentiment_analysis(row[1].lower())))def preprocess(input_file, output_file, keep=1): with open(output_file, 'w') as csvoutfile:
csv_writer = csv.writer(csvoutfile, delimiter=' ', lineterminator='\n')
with open(input_file, 'r', newline='') as csvinfile: #,encoding='latin1'
csv_reader = csv.reader(csvinfile, delimiter=',', quotechar='"')
for row in csv_reader:
if row[0].upper() in ['POSITIVE','NEGATIVE','NEUTRAL','MIXED']:
row_output = transform_instance(row)
csv_writer.writerow(row_output)# Preparing the training dataset
preprocess('BetSentimentTweetAnalyzed.csv', 'tweets.train')
向上采样以抵消类别不平衡。
当一个标签比其他标签出现得更频繁时,就会出现类别不平衡的问题。在这种情况下,分类器往往会被大类淹没,而忽略小类。
应用于我们的英语推特数据集[2],我们注意到中性与积极/消极类别的不平衡。因此,将所有事物分类为中性的原始策略将给出 73%的准确率(见下表)。出于同样的原因,我们的模型可能倾向于中性。如果不加以管理,类别不平衡将使我们的模型简单化和不准确。
Example of imbalance in labels
为了解决这个问题,我们必须使用上采样。上采样(或过采样)包括为少数类添加新的推文,正的和负的,以使它们达到与多数类相等的推文数量,这里是中性的。我们提供了一个简单的代码来做到这一点。
def upsampling(input_file, output_file, ratio_upsampling=1):
#
# Create a file with equal number of tweets for each label
# input_file: path to file
# output_file: path to the output file
# ratio_upsampling: ratio of each minority classes vs majority one. 1 mean there will be as much of each class than there is for the majority class.
i=0
counts = {}
dict_data_by_label = {} i=0
counts = {}
dict_data_by_label = {}# GET LABEL LIST AND GET DATA PER LABEL
with open(input_file, 'r', newline='') as csvinfile:
csv_reader = csv.reader(csvinfile, delimiter=',', quotechar='"')
for row in csv_reader:
counts[row[0].split()[0]] = counts.get(row[0].split()[0], 0) + 1
if not row[0].split()[0] in dict_data_by_label:
dict_data_by_label[row[0].split()[0]]=[row[0]]
else:
dict_data_by_label[row[0].split()[0]].append(row[0])
i=i+1
if i%10000 ==0:
print("read" + str(i))# FIND MAJORITY CLASS
majority_class=""
count_majority_class=0
for item in dict_data_by_label:
if len(dict_data_by_label[item])>count_majority_class:
majority_class= item
count_majority_class=len(dict_data_by_label[item])
# UPSAMPLE MINORITY CLASS
data_upsampled=[]
for item in dict_data_by_label:
data_upsampled.extend(dict_data_by_label[item])
if item != majority_class:
items_added=0
items_to_add = count_majority_class - len(dict_data_by_label[item])
while items_added<items_to_add:
data_upsampled.extend(dict_data_by_label[item][:max(0,min(items_to_add-items_added,len(dict_data_by_label[item])))])
items_added = items_added + max(0,min(items_to_add-items_added,len(dict_data_by_label[item])))# WRITE ALL
i=0with open(output_file, 'w') as txtoutfile:
for row in data_upsampled:
txtoutfile.write(row+ '\n' )
i=i+1
if i%10000 ==0:
print("writer" + str(i))upsampling( 'tweets.train','uptweets.train')
通过上采样,你冒着过度拟合的风险,一遍又一遍地重复同样的推文。但是如果你的数据集足够大,这应该不是问题。
使用 fastText 进行培训
现在是有趣的部分。是时候训练我们的机器了!
我们使用 fastText python 包装器来训练我们的模型。你可以在脸书研究院的 GitHub 页面上找到实现示例和文档。请确保您使用“git clone …”安装 fastText,而不是使用“pip install fasttext”。
由于我们已经准备好了数据,现在我们需要做的就是使用函数fastText.train_supervised
。此功能有大量选项,但为了简单起见,我们主要关注以下内容:
- 输入:我们训练数据的路径。
- lr :学习率。我们将其设置为 0.01。
- epoch :我们遍历整个数据集的次数。我们用 20。
- wordNgrams :一个 n 元文法是来自给定文本样本的最大 n 个单词的连续序列。我们把它设置为 2。
- dim :字向量的维度。我们用 20。
以下 python 代码显示了我们模型的训练。
hyper_params = {"lr": 0.01,
"epoch": 20,
"wordNgrams": 2,
"dim": 20}
# Train the model.
model = fastText.train_supervised(input=training_data_path, **hyper_params)
print("Model trained with the hyperparameter \n {}".format(hyper_params))
一旦经过训练,我们需要评估我们的模型在情感分析方面有多好。为此,我们可以使用两个度量精度和召回率,它们是 fastText 函数model.test
的输出。然而,由于我们的问题的性质,精度和召回给出类似的数字,我们可以只关注精度。
下面的代码对训练和验证数据执行model.test
,以比较我们模型的准确性。请注意,为了验证,我们采用了不同的数据集,在该数据集上我们使用了相同的清理过程,但没有上采样。
# CHECK PERFORMANCE
result = model.test(training_data_path)
validation = model.test(validation_data_path)
# DISPLAY ACCURACY OF TRAINED MODEL
text_line = str(hyper_params) + ",accuracy:" + str(result[1]) + ",validation:" + str(validation[1]) + '\n'
print(text_line)
总的来说,该模型对训练数据给出了 97.5%的准确度,对验证数据给出了 79.7%的准确度。
考虑到我们没有调整超参数,还不错。此外,研究估计人在判断对一段特定文本的情感时,只有 同意约 60%到 80%的次数 。因此,虽然我们可以尝试达到 100%的准确性,但我们必须记住,人类是会犯错的……最重要的是,我们在推特上工作!!
结论
我们刚刚展示了 fastText 如何工作,以及如何训练一个英语情感分析模型。我们使用了由AWS comprehensive产生的情感数据。在的另一篇文章中,我们解释了如何使用 AWS Elastic Beanstalk 和 Python Flask 应用程序为您的模型提供健壮的云基础设施。
如果你想重现结果,只要去我的 gitHub。如果需要完整的英文数据集,可以问我(对 GitHub 来说太大了)。我很乐意与你分享它。
成为中等会员,在平台上支持我!
https://medium.com/@charlesmalafosse/membership
参考
[1] 高效文本分类的锦囊妙计, 阿曼德·茹林,爱德华·格雷夫,彼得·博亚诺夫斯基,托马斯·米科洛夫,2016
https://github.com/charlesmalafosse。我的 GitHub 页面,包含本文的全部代码。
[3]脸书 GitHub,带有 fastText python 包装器。https://github . com/Facebook research/fast text/tree/master/python
[4]用 AWS Elastic Beanstalk 部署一个机器学习模型https://medium . com/@ charlesmalafosse/Deploy-a-machine-learning-model-with-AWS-Elastic Beanstalk-dfcc 47 b 6043 e
深度学习中的“深度”——一种实用方法
“深度学习”是难以捉摸的,但只要有一点数学知识就可以接近。这引发了一个实际问题:初等微积分足以解释深度学习吗?答案确实是肯定的。带着无限的好奇心去学习和重新学习新的和旧的,如果你能有条不紊地遵循下面的章节,我认为你会跨越鸿沟,直观地理解和应用每一个概念,包括微积分,以消除深度学习的所有复杂性。
学习“深度学习”既有趣又令人担忧。数学是基础,可以帮助清楚地理解和不可磨灭地蚀刻概念。当我开始阅读迈克尔·尼尔森的书“神经网络和深度学习”时,我一开始不小心完全理解了许多需要复习的基本概念,我想如果我可以与他人分享我的旅程以简化他们的学习,那么我想我已经为促进深度学习尽了自己的一份绵薄之力。
在这篇文章中,我将讲述我所采取的步骤以及我所研究、阅读和理解的内容——尽可能直观地揭示每个概念以及引起你兴趣的其他主题。每一步都在一个标题下详述,以理解深度学习的整个架构,这是仍在发展的人工智能的基石。随意跳过熟悉的部分,深入研究感兴趣的内容。
测量深度的步骤:
- 开端——用感知器模拟决策
- 节点内部的主力——激活功能
- 稍微绕了一下基础——微分学
- 基础——基本统计和损失减少
- 大优化—梯度下降
- 救援直观示例—下降揭秘 d
- 系综直接向后&向前—前馈&反向传播
- Bare neural net 的内部工作——与代码匹配的矩阵
- 学习曲线回溯—参考文献&致谢
开始——用感知器模拟决策
生物神经元模型解释了神经系统促进感知(无论是感觉还是其相关活动)的潜在机制。从这种感知中产生了“感知器”,一种寓言式的数学单位或模型。简而言之,感知器(1)接受多个输入,(2)加权(乘以一个设定的权重),(3)求和并累加到一个称为 bias 的常数,最后(4)发送到一个激活函数以(5)提供一个输出。
通常你可以把感知机想象成一个数学模型,在给定一组输入和一个阈值的情况下,决定我今天是否可以去看电影。假设输入是云量、假日事件、一天中的时间,所有这些都是一个按比例缩放的实数,每个都通过乘以相应的预设权重进行修改,并与阈值(偏差因子)相加,然后这个组合数被步进或平滑,其中步进函数的结果是 1 或 0,而平滑函数的值在 1 或 0 之间,这可以解释为结果发生的概率。
为了清楚起见,下面是上面例子的一个说明:
从数学上来说,如果我们有 j 个感知器输入,输出表示为:
为了进一步简化,我们可以将上面的等式视为点积或标量积,这是一种代数运算,采用两个等长的数字序列(通常是坐标向量)并返回一个数字。两个向量 w = [ w 1、 w 2、…、 wn 和 x = [ x 1、 x 2、…、 xn 的点积定义为:
以这种方式表达上面的例子,一个 1 × 3 矩阵(行向量)乘以一个 3 × 1 矩阵(列向量)得到一个 1 × 1 矩阵,用它的唯一条目来标识:
从上图中,我们可以直观地将感知器网络想象成一个可以连续过滤前一层的网络,即,对前一层的决策进行加权和决策,以此类推,直到我们到达作为输出的最后一层。有时,输入可以被表示为一个感知器,只有一个固有的或恒定的偏差,没有权重或指向它的输入。早期,当感知器模型被创建时,人们认为 NAND 或 XNOR 不能被模拟,这使得创新的步伐处于休眠状态,后来人们发现所有的位运算都可以被模拟,或者事实上是展示任何模式的函数。随着多层网络变得明显,深度学习终于打破了它的休眠,并迅速收拾残局。
节点内部的主力—激活功能
对任何过程建模都是测量输出与给定输入的基本活动,以获得理解,更重要的是保证在已建立模型的情况下给定输入的结果的可重复性。本质上这个模型对我来说总是用简化的术语来说就是“曲线拟合”。有了输入和模型,您总是可以确定输出是什么。激活函数是“曲线拟合器”,在给定输入的情况下,它们根据定义的模型提供输出。同样,模型的变化导致不同的激活函数。
步进功能
一个基本的激活功能是步进功能,如下图所示:
方程式:
步进器或二进制是一个输出为 1 或 0(是或否)的函数。或者,它也被称为 Heaviside 阶跃函数或单位阶跃函数。该函数为所有负输入发出 0,为所有正输入发出 1。感知器使用这种激活来根据输入发出是否继续的信号。
线性函数
顾名思义,线性函数将给定的输入增加一倍。数学上,线性函数是其图形是直线的函数,即一次或零次多项式函数。
方程式:
该图表示线性函数 f(x) = 4x,本质上对输入有 4 倍的乘数效应。每当我们想要按比例缩放输入时,这在神经元中是有用的,但是它的用例很少。
Sigmoid 函数
Sigmoid 是一条 S 曲线,其中所有正值的输出都= 0.5,输出永远不会超过 1。对于 x 轴和-x 轴的较高值,y 值是一个渐近线。S 形的顶部是凹的,底部是凸的。sigmoidal 函数的定义非常广泛,但唯一的是它有一个实值输出,一个拐点和一个钟形一阶导数。
方程式:
一般来说,Sigmoid 函数是从逻辑函数获得的,逻辑函数的常见形状是“S ”,其等式为
在哪里
- e =自然对数底(也称为欧拉数),
- x0 =形中点的 x 值,
- L =曲线的最大值,以及
- k =曲线的逻辑增长率或陡度
在标准逻辑 sigmoid 函数的情况下,值将如下:L = 1,k = 1,x 0 = 0,给出上面的 Sigmoid 方程。
。这个函数的一个很大的优点是它是非线性的。在-3 和 3 的范围内显示线性,在该范围的其余部分逐渐变细到渐近线。它可用于将连续空间值转换为二进制范围之间的值,这种离散化方法对于抑制极端情况非常有用,其特征是在低激活时极限值为零,在高激活时极限值为一。目前,这个函数足以开始我们的神经网络之旅,但我们稍后将全面探讨其他函数及其适用性。
Sigmoid 优化背后的理念
对 sigmoid 的兴趣在于它可以在同一个函数中管理线性和非线性建模。在上图中,拐点在 0.5 处,由 z 表示。Sigmoid 曲线模拟了典型的规模经济问题。例如,如果制造商开始制造玩具,初始投资很大,回报率很低或接近于零,一旦公众开始购买玩具,销售额逐渐增加,并随着需求的增加而呈线性增长,此时供应不能完全满足需求,利润飙升至供应赶上需求的拐点,此时利润理想,供应超过需求,无论施加多大的影响,利润率都会下降,不能超过最大值。
这推测任何依赖于阈值的活动都是通过 sigmoid 函数进行优化的理想候选。让我们看看几个这样的候选对象,并将其与它如何帮助深度学习用例联系起来。如果竞选经理试图优化他们的广告支出,那么他们的目标函数将模拟一个结果,在这个结果中,他们希望把钱花在可能改变投票支持他们的候选人的州上。而花在最终失败的州上的钱则完全浪费了。Sigmoidal 函数也会以同样的方式运行,只建议在一个州花钱,如果他们能通过获得大多数选民的门槛。他们会预测,在一个很有可能受到影响的州投入大量资金,要比在两个州各投入一半资金要好,因为在这两个州,他们最终可能会两败俱伤。在从给定图像预测数字的“深度学习用例”中,每个像素或像素组共同跨越一个阈值将是有用的,如果使用分层 sigmiod 函数连续交叉验证它们,那么它可以以更少的错误完美地预测结果,并且在此过程中,我们的模型将学习得很好。
关于基础的一个温和的迂回——微分学
区别
在我们深入研究之前,我们必须回忆一下我们在高中数学中学习的关于微积分的知识——这对我们外行的大脑来说是一个可怕的主题,那么,这是一个带着乐趣和功利主义心态进行反思的完美时间,而不是我们在学校时代所做的死记硬背。
衍生品就是变化率。你从家到目的地需要多长时间?这些年人口增长有多快?人们有多富有?所有这些都在谈论变化率,一个对另一个。与上面的例子相比,它们是距离对时间,计数对年,美元对人计数。
顺便提一下,变化率也是斜率,如果将上述用例数据之一绘制在 x-y 轴、y 轴上的距离和 x 轴上的时间的图表中,则两个时间点之间的平均速度由所用时间内行驶的距离给出,这实际上就是斜率。如果你真的想找到某个特定时间点的速度,很难测量在无限小的时间片上走过的距离,因此没有办法计算变化率。但是差异化在这里很有帮助。
受到对复杂数学概念的清晰而简单的解释的启发,让我们借用一下学习的概念(感谢 MathIsFun site,by Ed。罗德·皮尔斯)以更清晰、有趣和简洁的方式解释衍生品。请确保你仔细阅读了每一篇文章,以回忆/刷新你当时所学的内容,并将微分学内化,这对理解神经网络的功能是至关重要的。
- 导数介绍 —讲述变化率,它与斜率的关系,我们如何得出给定函数的斜率
- 导数规则 —涵盖管理各种类型函数导数的规则。
- 幂法则——将幂函数去杂波的最简单方法
- 二阶导数 —理解速度、加速度和加加速度的二阶导数的绝妙方法
- 偏导数 —实际使用案例,使用我们熟悉的圆柱体了解偏导数的真实用途
- 可微分 —该测试允许检查曲线的可微分性,以确定斜率的存在
- 使用导数寻找最大值和最小值 —学习梯度下降和反向传播的基础
微分就绪计算器
Image Credit: MathIsFun
Image Credit: MathIsFun
如何理解衍生品:积、幂&链规则 —提供了一种直观的方式来理解我们在高中时一直记忆的积、幂和链规则。有了对微分学的坚定理解,我们再下一步!
基础——基本统计数据和减少损失
统计数字
在统计学中,您应该熟悉对位置和可变性的估计,这对于数据如何表示和变化以及如何缩放数据以实现标准化和加快分析至关重要。与这一“深度学习”之旅相关的基本概念的快速总结:
位置估计
具有测量数据的变量可能有数千个不同的值。探索数据的一个基本步骤是获得每个特征(变量)的“典型值”:估计大部分数据的位置(即它们的集中趋势)。
- 表示 所有数值的总和除以数值的个数
替代:平均值 - 加权平均值 所有值的总和乘以一个权重除以权重的总和。
替代:加权平均 - Median 使一半数据位于上下的值。
备选:第 50 百分位 - 加权中值 使权重之和的一半位于排序数据上下的值
- 稳健 对极值不敏感。
替代:抗性 - 离群值 与大部分数据差异很大的数据值。
替代:极值
可变性的估计
位置只是概括一个特征的一个方面。另一个维度是可变性,也称为离差,衡量数据值是紧密聚集还是分散。统计学的核心是可变性:测量可变性,减少可变性,区分随机可变性和真实可变性,识别真实可变性的各种来源,并在存在可变性的情况下做出决策。
- 方差
- 平均值绝对偏差
平均值与平均值偏差的绝对值的平均值。 - 范围
数据集中最大值和最小值之差。 - 百分位数 该值使得 P 个百分比的值等于或小于该值,而(100-P)个百分比等于或大于该值。
- 四分位距 第 75 个百分位和第 25 个百分位之差
数据缩放
来自维基百科:由于原始数据的取值范围千差万别,在一些机器学习算法中,目标/损失函数如果不进行归一化将无法正常工作。例如,大多数分类器通过欧几里德距离计算两点之间的距离。如果其中一个要素的值范围很大,则距离将由该特定要素决定。因此,应该对所有要素的范围进行归一化,以便每个要素对最终距离的贡献大致成比例。
应用要素缩放的另一个原因是,使用要素缩放时,梯度下降(我们稍后将更详细地了解这一点)的收敛速度要比不使用要素缩放时快得多。
- mix-Max 的最小-最大缩放比例 公式:
这种方法在一个范围内(比如[0,1])重新缩放单个数据点。假设我们有一个包含 100 个数据点的数据集,x 是一个单独的数据点,min(x)是 100 个数据点中的最小值,max(x)是其中的最大值,min-max 缩放将每个 x 值压缩(或拉伸)到范围[0,1]内。有时该范围可以设置为[-1,1]。目标范围取决于数据的性质和分析中采用的算法。下图给你一个直观的说明。
Image Credit : Feature Engineering by Alice Zheng
单位换算或 L2 归一化 单位换算公式:
这是通过将每个数据点除以距离中心或平均点的向量的欧几里德长度而获得的。L2 范数测量坐标空间中向量的长度。勾股定理给出了距离
由于这种划分,数据在-1 到 1 的范围内正常化。
Image Credit : Feature Engineering by Alice Zheng
方差缩放或标准化 标准化公式:
平均值直观地表示数据的绝对分布,而标准差(SD/sd)给出了加权分布的概念。当数据的分布围绕平均值对称,并且其方差相对接近正态分布时,分布的度量是最有用的。(这意味着它是近似正常的)。在数据近似正常的情况下,标准偏差有一个规范的解释:68%的数据将在 1 个标准差内,95%在 2 个标准差内,99%在 3 个标准差内。问题是分布是宽还是紧?这给出了数据和可能出现的异常值的概念。
在这里,对于每个数据点,平均值被去除,并通过它们的标准偏差进一步加权。从本质上来说,是根据数据点的总体分布来调整每个数据点的中心值。
得到的缩放数据点被标准化为平均值为 0,方差为 1。如果原始特征具有高斯分布,则缩放后的特征是标准高斯,也称为标准正态。下图直观地说明了这个概念。
Image Credit : Mastering Feature Engineering by Alice Zheng
损失函数
可变性激发了预测偏差的测量方式,与标准偏差类似,损失可以被测量、缩放和标准化。损失函数的核心是衡量你的预测与原始值相比表现如何。现在,您有了一个工具来跟踪模型性能,同时调整其性能,以便将损失降至最低。非常基本的损失函数是找到原始值和预测值之间的绝对差值。这提供了对单个预测的良好判断(绝对误差— AE ),并且同样可以对所有预测进行平均,以获得平均绝对误差(MAE / L1 损失),并且平均它们的平方误差给出均方误差(MSE / L2 损失/二次损失)。
中小企业与制造业——一项分析
我们来取一个简单的函数 y = f(x) = 4(x),针对这个函数画一个图如下。还绘制了相应的预测值 y,以显示与实际值的偏差以及损耗、绝对损耗和平方损耗。还给出了统计值以及平均误差和均方误差。
这些公式是:
MSE=
MAE=
y^p=预测值(predicted),y =观测值(actuals)。
如果公式不是你的主食,你可以通过
MSE 找到 MSE & RMSE
- 平方残差/损失。
- 求残差/损失的平均值。
RMSE
- 取结果的平方根。
MAE 和 MSE 都表示在不同的感兴趣单元中的平均模型预测误差稍有不同。这两个指标的范围从 0 到∞并且与方向误差无关。它们是负校准分数,这意味着值越低越好。取平均平方误差或简单平方误差的平方根有一些有趣的含义——因为误差是平方的,并且在它们被平均之前,MSE/RMSE 给大误差一个相对高的权重。这意味着当大误差特别不理想时,MSE/RMSE 应该非常有用。
查看上图,我们可以看到实际值和预测值的 MAE 和 MSE (RMSE 就是 MSE 的平方根)值。除了误差非常高的 2 个异常值之外,大多数预测是内联的。与 MAE 相比,MSE/RMSE 将会给异常值很大的权重,因为你会注意到平方损失远远高于他们更接近的损失。直观地,我们可以从损失函数角度解释预测性能,其中如果试图最小化 MSE,那么预测应该是所有目标值的平均值。但是如果我们试图最小化 MAE,这个预测将是所有观测值的中间值。中位数比平均值对异常值更稳健,这使得 MAE 比 MSE 对异常值更稳健。
使用 MAE 损失(特别是对于神经网络)的一个大问题是其梯度始终相同,这意味着即使损失值很小,梯度也会很大。这对学习没有好处。为了解决这个问题,我们可以使用动态学习率,它随着我们接近最小值而降低。MSE 在这种情况下表现很好,甚至在固定的学习速率下也会收敛。对于较大的损失值,MSE 损失的梯度较高,并且随着损失接近 0 而降低,使其在训练结束时更加精确(见下图)。)
MAE 损失
Image Credit: Heartbeat — MSE & MAE Loss
MSE 损失
上面是 MAE & MSE 函数的图,其中真实目标值为 100,预测值的范围在-10,000 到 10,000 之间。MSE 损失(Y 轴)在预测值(X 轴)= 100 时达到最小值。范围是 0 到∞。
神经网络中的一些损失函数
损失函数在稳定和提高给定模型的预测精度方面起着至关重要的作用。它们被用于将实际值和预测值之间的差异最小化。下面列出了深度学习中使用的损失函数的总结——名称、方程和它的作用要点。
请注意,y 是实际点或节点数据,yhat 是 y 的预测或函数输出,C 是所有数据点的总误差。
在回归中广泛使用,数据点的合成拟合线应该是使每个点到拟合回归线的距离总和最小的线。实际值和预测值之间的平方差称为“残差”,损失函数的目标是最小化 DL 中的残差平方和。
顾名思义,它是 MSE 值的一半,这是为了方便计算,我们稍后会讲到。
简单地求和,不求均方误差的平均值。也叫 L2 损失
它测量实际和预测的对数值的方差差异。当预测值和实际值都很大时,这很有用-使用对数的力量将指数值减少到渐进值。MSLE 对低估的惩罚多于高估。
实际值和预测值之间的绝对差值。用 MSE 计算一个好的梯度更容易,而 MAE 只给出一个常数。
很明显,实际值与预测值的百分比误差超过了实际值。当实际值为零时,不能使用。当实际和预测之间的差异很大时,它会超过 100%。该函数倾向于选择预测值过低的模型。
MAE 没有平均值,即没有除以 n
通常在二进制分类和神经网络中,输出是表示预期结果的概率的真实值,而不是给定事件的 0 或 1。或者可以很容易地计算出来——给定一组数据点,测井曲线损失衡量实际与预测的概率分布的差异。我们还可以假设实际数据点和预测数据点代表一种概率分布。从二进制分类的角度来看,y 是事件 A 或 B 的非此即彼标签(如果 A 发生,则为 1;如果不发生或 B 发生,则为 0 ), p(y)是其概率。左手边代表,标签乘以标签为 A 的概率,而右手边 1-标签值乘以为 b 的概率。进一步的直观解释此处和参考此处为从 softmax 导数的良好推导。
当模型输出是每个类别的概率而不是最有可能的类别时-其中它处理多类别,即每个输出将表示例如每个输出是猫、狗还是老鼠。Lj Miranda 博客中的理解 softmax 和负对数似然是一篇关于 soft-max 和 NLL 的好文章。我完全被那篇文章中的精彩画面迷住了。
Image Credit: Lj Miranda Blog
将以上两者联系起来,直接阅读直观的文章以获得简洁的解释。
从高中开始,我们知道 Cos 0 是 1,Cos 90 是 0,这表明余弦接近于 0 的给定的一组点比接近于 1 的点相似,在 1 的点上它们是完全发散的。这样可以忽略幅度差异,只考虑角度收敛。
损失及其对误差减少的影响以及与梯度下降的比较在这里详细讨论。接下来我们将更直观地处理梯度下降。
大优化—梯度下降
梯度下降
在提到 ML/DL 时,经常提到的算法是梯度下降法。因为它是如此的基础,所以大部头的书都已经涵盖了对它的说明、解释和驳斥,但是这里我们将通过两个例子来更直观地理解它,而不是你遇到的花哨的 3D 图表和动画。
在我们深入研究一个例子之前,梯度下降字面上是“一种穿越的方式”,这样我们就到达了谷底。形象地说,我们必须下降,并检查我们是否在最低处,如果不是,寻找下一个最佳下降方向,并重复迭代。一个警告是,如果我们被困在一个最低的最小值,但不是最低的(假设我们有一个可以看穿岩石的激光视力),然后折回并开始在将导致最低最小值的方向,全球最小值在谷底深处,谷底点。
假设我的山谷没有扭曲,是一维的,看起来像代表 y = x^2 的曲线,那么我们可以在下图中画出这个山谷。回想之前使用一阶和二阶导数找到最大值和最小值的过程。这里,斜率向右增大,向左减小。参考两条曲线,第一条是曲线 y = x^2,下一条是曲线 y = 1+2x-x^2.下图说明了这个想法。
了解坡度&在基本曲线上下降
正如我们注意到的,在上面的左图中,斜率是正的,并且随着我们向右移动而增大,随着我们向左移动而减小。为了找到全局最小值,这是一个简单的操作——在斜率图中直观地查看斜率达到零的位置,或者在数学上,如果斜率方程等于零,我们得到 x = 0,斜率最小的点是全局最小值。你可以在 x (=0),二阶导数= 2 这一点上应用二阶导数测试,当它大于 0 时,它是局部最小值,并且是一维的,它也是全局最小值。还要注意没有迭代过程,因此一维函数本身没有梯度下降。同样,右图给出了一个想法,当我们冒险寻找全局最大值时,抛物线方程会发生什么。这里,二阶导数= 2–2x,当 x = 1 时,斜率为零。应用二阶导数测试,在这一点上是-2,我们知道这是一个一维函数的局部最大值,也是全局最大值。
二阶导数不可能为零,除非 y = x(如上图所示),这里斜率是常数,没有最小值或最大值点,而是一个技术上未确定的点,称为鞍点。鞍点发生在多维函数中。
另一个重要的直觉是,在一维函数中,我们向左或向右移动,但从另一个角度来看,如果当斜率为正时,你向相反方向移动,你将达到最小值,即当斜率在某一点最陡时,然后向相反方向移动,陡度降低并达到零。这在多维函数的梯度下降中很方便。为什么重要——我们可以简单地将该点的最陡斜率乘以一个小因子,然后减去/加上,将 x 轴点向相反方向移动,并重新评估函数的斜率,看它是否变得不那么陡,并达到最小值,然后重复这一过程,直到达到最小值。
救援的直观例子——下降揭秘
简单函数 f(x,y) = x + y 的梯度下降
是时候回忆一下我们之前研究过的偏导数了。如果 f(x,y,z)= x ^ 4-3 XYZ,那么用卷笔字表示法:
现在让我们考虑一个函数 f(x,y) = x + y。将 x 和 y 的值分别初始化为 2 和 4,输出将是 6。
这里招手的是,如何调整 x 和 y 的值使泛函输出最小?公式很简单:
- 新 x 位置=旧 x 位置—(小常数)* fn w . r . t x 的斜率
- 新 y 位置=旧 y 位置—(小常数)* fn w . r . t y 的斜率
让我们用它的分量来区分 f(x,y)
这些偏导数可以被认为是作用在 x 和 y 输入上产生输出的力。当我们产生输出时,目标是达到最小或零输出(顺便提一下,此时 x 和 y 的斜率为零,二阶导数为+ve——回想一下我们之前的最小值/最大值探索,这里的输出和损失函数也是一样的)。让我们看看如何应用上述公式,我们假设 delta = 0.01。
x 和 y 的新位置导致 f(x,y) = x + y 的值为@ 1.99 + 3.99 = 5.98。与我们的起始值 6 相比,它略小。为了达到值 0,我们将利用梯度下降的重要特征,即迭代步骤,直到我们达到收敛。过程是重复的
直到 x+y 为零。因此,如果我们重复上述步骤 300 次,我们将得到 x = -1 和 y = 1,最终输出 x+y = 0,我们最终得到的 x 和 y 的值将给出最小值 0。
函数 f(x) = mx + C 的梯度下降
让我们举一个简单的例子,沉浸在内部工作,因此可以清晰地上升梯度下降。
根据在城市中央商务区(CBD)进行的一项调查,收集并在栏 A 和栏 B 中列出了办公空间租金价格,分别确定了以平方英尺为单位的办公空间面积(X)与以千美元为单位的每月租金收益率(Y)。
问题陈述:对于 CBD 的一个新办公空间,给定建筑面积,每月租金收益是多少?
绿线描绘了当前收益率价格与地板覆盖面积的关系。我们将使用一个简单的线性模型来预测给定其建筑面积(X)的新租金收益率(Y pred)。琥珀色线描述了预测给定区域产量的模型
Y pred = mx + C
绿线给出了基于当前调查的实际租赁收益(Y actual)。
绿色点和琥珀色点之间带有互连线,表示 Y 实际值和(Y 预测值)之间的差异,即预测损失或误差(E)。
我们有责任找到 C 和 m 的最佳值(称为权重),使其最适合 Y pred = mX + C 支配的预测线,以减少预测误差并提高准确性。我们将采用二次损失函数 RMSE 作为损失函数,考虑到实际值和预测值,需要将其最小化。
简单的说 HMSE =,
让我们利用梯度下降算法来找到权重 C 和 m 的最佳值,以最小化 HMSE。
这里列举了所涉及的步骤:
- 使用最小-最大归一化法将 A 列和 C 列中的数据分别归一化到 D 列和 F 列中,并将用作 X 和 Y
- 将 C 和 m 初始化为随机值,并计算每个数据点的平方误差(SE)
- 当 C 和 m 相对于原始随机值变化很小时,单独计算每条线/数据点’ HMSE 变化’,总变化构成所有数据点所有这些变化总和。
求和后,我们可以将求和值除以数据点数来求平均值,但这是不必要的,因为它只是缩小了总变化,而且会影响我们随后引入的变化,如下一条语句所示。本质上,这可能会减慢收敛速度,因此我们可以停止 ar 求和,无需求平均值。在动态情况下,输入数据点的数量事先不知道,那么我们也不能平均,因此最好是左总结 - 调整相应的权重 C 和 m 减去乘以学习率的总“HMSE 变化”
- 使用新的权重来计算 se 的总量,并检查它是否在减少
- 重复步骤 3 和 4,直到总硒不再显著减少
详细步骤:
- 参考列 D、E&F——它们是对应于列 A、B & C 的最小-最大归一化租金数据
- 要拟合 Y pred = C + mx 线,首先使用随机种子值并计算预测损失 HMSE。HMSE 在所有数据点上,当应用于单个数据点时,公式中的 n 被去掉——只需计算出每个数据点的半平方误差,然后求和。SE 和 HSME 在这方面指的是同一个。
3.计算重量的误差梯度 w.r.t,在我们的例子中是 C 和 m
我们稍后将分别对上述方程进行演算。上述两个等式给出了“C 和 m”的变化,并且是简单的梯度,其将运动方向从 w.r.t .指向 SE。参考最后两栏的计算
4.用梯度改变权重 C 和 m,以达到最小的最佳 SE 值。
我们将调整 C 和 m 的初始随机值,使我们朝着最优 C 和 m 的方向前进。
更新规则:
这里 lr 是学习率,因此:
New C = 0.52–0.01 * 5.040 = 0.47
New m = 0.81 = 0.01 * 2.279 = 0.79
5.使用新的权重来计算 se 的总和并检查其是否减少-检查上图中 HSME 值 id 的总和是否减少
6.随着步骤 3 和 4 的重复,直到总 SE 不再连续显著减少,然后我们达到了具有最佳预测率的最佳 C 和 m
梯度下降背后的微积分
平方损失函数
每个数据点的 HMSE 最终应用
一阶导数是 w.r.t 'C '是
现在让我们算出偏导数:
应用不同的损失函数
如果您决定在上述努力中应用不同的损失函数,我们可以尝试 Log-Cosh 函数,它也支持一阶和二阶导数。如果你应用 MAE 或 RMSE,两者都产生一个常数,因此从学习和收敛的角度来看,在神经网络梯度下降过程中是有益的!
二阶导数是简单地将上述项乘以 x
梯度下降变体
批量-普通梯度下降
如在之前的沉浸式示例中,有 12 个数据点,并且模型参数 m & C 仅在结束时更新,即,在针对 12 个数据点的整个记录集计算通过每个数据点的所有导数和损失值之后。本质上,这是普通的批处理 GD,其中模型参数仅在所有数据点计算完成后更新——处理所有数据点被视为一个时期。
小批量—梯度下降
在上面的例子中,在 12 个数据点中,我们可以先处理 4 个数据点并更新模型参数,然后是接下来的 4 个数据点来更新参数,依此类推,直到所有的数据点都用完为止。基本上,在每个小批量(在我们的例子中是 4 个数据点)被处理之后,模型参数被更新,因此在一个时期内,参数被更新 n 次,其中 n =总数据点/小批量数据点。
随机梯度下降法
通常,SGD 使用单个数据点计算梯度,但大多数 SGD 应用实际上使用几个样本的小批量。因此,在每个时期开始之前,所有数据点都被随机打乱,然后进行小批量处理。对于具有大量局部最大值/最小值的误差流形,SGD 工作得很好(与批量梯度下降相比,现在有种高级方法)。这样,使用减少数量的数据点计算的稍微更嘈杂的梯度倾向于将模型从局部最小值中拉出到希望更优化的区域中。单个数据点确实有噪声,而小批量数据往往会平均消除一点噪声。因此,在使用小批量时,加加速度会降低。当小批量可以避免一些不良的局部最小值,但足够大以包括全局最小值或性能更好的局部最小值,并且理想地引导我们更容易落入放置最佳最小值的更大和更深的吸引盆地时,可能可以达到良好的平衡。
SGD 类似于对选民的横截面进行抽样以获得预测,而不是进行实际的选举。与批处理相比,学习率是惊人的,假设我们的总数据点是一百万(1M ),如果小批量是 10K,那么我们得到的加速因子是 1M/1K = 1000。加速因子可能不准确,并包括统计波动,但足以引导成本函数逐步降低。SGD 的一个好处是计算速度更快,并且可以并行化。
当构建梯度下降算法时,可以设置最大历元计数(迭代计数)以及损失因子差,它们协同工作以达到收敛或停止无休止的处理,无论哪一个先发生。点击进一步探索这些变体。
系综引导的来回-前馈&反向传播
链式法则
在我们之前的梯度下降示例中,我们看到了一个简单的函数 y = mx + C。这可以想象为一个单节点激活函数,它简单地采用一个输入来提供一个输出,本质上是将输入缩放 m 并添加一个偏差 c。
现在让我们想象一组这样的激活函数相互连接,一个的输出是下一个的输入。这是一个简单的线性网络,可能只有很少的节点。在我们的例子中,我们将考虑如下所示的 4 节点线性网络。同样的想法也适用于此——我们需要找到正确的权重和偏差,以便优化整体函数的输出,使原始输出和“互连函数”输出之间的任何误差最小。
为了演示这个想法,请看下图——这是一个具有输入节点和输出节点的两层隐藏式神经网络。
每个节点都是与其相连的前一个节点的功能。如果我们要更改输入节点中的输入或权重值,这种更改将向下级联,因为每个节点都将更改它们接收的输入,并最终反映在输出中。因此,输出变化可能看起来很复杂,但直觉上每个节点都会放大级联变化。由于每个节点相互依赖并且具有函数依赖性的概念,我们可以用数学公式表示一个复合函数来表示该网络的行为:
将所有等式合并为一个等式:
由于中间节点不是单独工作的,并且依赖于来自前面节点的输入,如果我们想要了解 w1 的微小变化对输出的整体影响,那么我们必须对输出 w1 从最后一个节点到第一个节点依次应用导数。合成的复合导数将是:
让我们在这里暂停一下,看看如何利用链式法则的详细例子。
链式法则在起作用——用一个例子来理解
微分链规则是反向传播的基础,获得直观的理解对全面理解神经网络大有帮助。通过下面的例子,我们可以解释链式法则是如何工作的,并了解因素的变化如何导致最终产出的变化。在下面的图表中,我们有 3 个以线性方式连接的激活函数,基本上任何输入通过它们级联,以给出遵循所述规则的输出。很容易理解:
- i/p 表示整个线性网络的输入
- 任何 i/p 被 w1 加权并补充以偏置 b1,共同表示为 z1,被馈送到激活功能#1
- 该函数将任何输入缩小 1/8 倍,得到 h1 作为输出
- 相同的模式重复,直到输出 h3
- 最后,我们有一个误差函数,其中如果我们知道实际 h3 和预测 h3,就可以计算误差/成本,但现在我们忽略这部分
让我们列举从输出到输入的上述线性网络的方程
- h1 = f1(w1 * x+B1)
=(w1 * x+B1)/8 - H2 = F2(w2 * h1+B2)
=(w2 * h1+B2)/7 - O = H3 = F3(w3 * H2+B3)
=(w3 * H2+B3)/9
如上所示,基本上你在 x 轴上的任何摆动都会按照我们的例子缩小,然后由于每一层的重量和偏差而略微上升。还有一点需要注意的是,“h”不知道 x 发生了什么,只是改变了“g”的变化,“g”也不知道 x 的变化,只是摆动“f”等等。现在 x 的任何变化都会被 f 的变化率放大,然后是下一层等等。
O = h(g(f(x)))h w . r . t . x 的变化为
链式法则不仅仅是分母和分子的单位相消——它是一个波动的级联,在每一步都可以放大、缩小或调整。链式法则适用于任何数量的变量(f 取决于 g 取决于 h,依此类推),只是级联摆动。把它想象成放大不同变量的视角——从 dx 开始向上看,你可以想象在波动到达 h 之前需要的整个调整链。
O w.r.t w1 的任何变化(w1 也是上述 x 的放大值)可以用链式法则写成,如下所示:
以上表格用数字数据总结了链式法则背后的思想
- A 列:考虑这个线性网络的几个输入
- B 列:计算 h1
- 列 C:计算 h2
- D 列:计算 h3 —最终输出
- E 列:根据 w1 的变化计算新的 h3—只需计算 h3 相对于 w1 的变化率,然后乘以 w1 的变化,最后将其加到 H3 上,即可得到新值
- 重复的行通过合并新的 w1 值并进行所有端到端计算来计算 h1、h2 和 h3,从而得出 h3 —注意 E 列数据和 D 列标题“新 h3”下的数据(在修改的 w1 上)是相同的
- F 列:使用近似值计算导数,如公式所示:
这里 O 只不过是计算 h3
用近似法和链式法则计算的值与图示相同
反向传播——反向链规则
在我们之前的线性节点网络中,让我们添加一个名为成本函数的最终函数。这将获得最终输出(使用给定模型(我们的线性网络)计算的 y 值也称为预测值),并计算与给定原始输出(原始 y 值)的差异。由于这是另一个函数依赖关系,现在我们的成本也受到输入、权重和偏差的影响,因此是它们的函数。
现在让我们在这个网络的末端附加一个“成本函数”,如上所示,很明显这个函数的输出也依赖于输入和中间权重。因此,我们也可以将相同的导数公式扩展到我们的成本函数:
在“通过示例理解链式法则”一节中,我们能够使用近似法找到复合导数。即通过将 w1 改变一个小值并计算新的输出,因此。
价值。
现在会出现一个问题,如何应用近似原理来计算真实神经网络中成本 w.r.t .权重的变化。走这条路是可行的,但是会导致计算速度变慢。让我们假设一个大的权重网络,为了找到成本 w.r.t w1 的变化,我们需要找到所有权重承载节点处的 C 和 C(具有δw1 ),对它们求和,得到它们的最终数量,然后应用近似公式。如果节点数以百万计,近似算法会嘎然而止,而链规则方法在将导数传播回网络时会很方便,通过梯度下降快速找到权重,以降低总成本,如下所示。
神经网络
网络
是时候深入研究真正的神经网络了,根据目前的理解,算出方程式,并将其应用到 python 代码中。符号和代码是基于由迈克尔尼尔森的开创性的书。我不得不花相当多的时间来理解 python 代码中的符号、等式和推论。我在这里所做的是用更好的可视化来进一步简化它,这样它就可以很快被吸收,并且这本书可以被用作参考资源。
想象一个 4 层的网络(如上所示):
- 第 0 层—3 个节点的输入层
输入没有转换,它们只是被传递 - 第 1 层——包括 4 个神经元
对于该层中的每个神经元,第 0 层输入被加权,最后添加一个偏置并馈入激活函数 - 第 2 层——包括 3 个神经元
对于该层中的每个神经元,第 1 层输入被加权,最后添加一个偏置并馈入激活函数 - 第 3 层——包括 2 个神经元
。对于该层中的每个神经元,第 2 层输入被加权,最后添加一个偏置并馈入激活函数
让我们给我们现在所说的赋予适当的符号,并用数学方法恰当地表达出来,这很容易理解。考虑该网络的输入用例也是很好的,但不需要完全匹配输入和输出,而是提供网络将如何处理整体数据的上下文和直观理解。
MNIST 是深度学习的标准火炬手数据集——它包含 60K 张手写数字的图像和每张图像的注释正确数值。
- 输入是一个 28×28 像素的图像
转换成一个 784 个输入的阵列,存储 0 到 1 之间的灰度值 - 输出表示 0–9 之间的数字
转换为 10 个输出的数组,每个输出为 0 到 1 之间的值,大于 0.5 的值表示该数字按顺序排列。在某种程度上,输出也可以被认为是该数字出现在 i/p 图像中的概率
数学表达的网络
一个完整的网络和完整的符号在这里给出,并得到一个完整的景观点击图片。
基本定义
现在,让我们一步一步地定义所有的基本要素——输入、权重、偏差,请参考下图
- 输入(在第一层)
输入= x
通常是原始的或归一化的数字数据,在这一层中用 x 表示。还要注意,在这一层中,没有激活、权重和偏差,因此所有这些都为零,我们也可以说在这一层中:
我们随后会看到 z 和 a 是什么
- 行和层(列) Rows = j 和/或k Layers =L或L
j始终与当前层关联, k 是指第 (l-1)层。L’表示最后一层或一层的集合。
还要注意的是,在上图中,神经元并没有从顶部等距排列,而是很好地放置在相应的行和层中,以匹配我们稍后将看到的矩阵表示。 - 重量
每个神经元从前一层神经元的输出中获得大量输入,然后将偏差合并为输入。j 和 k 符号和以前一样,如下图所示
- 偏置
来自前一层的所有加权输入被相加并加到 j 行中 l 层节点的偏置,被馈送到 l 层和 j 行的神经元。
其他定义
现在让我们一步一步地定义下一个层次——组合输入、激活、输出、成本函数,请参考下图。
- 神经元的输入
- 激活
- 成本
- 从 MNIST 数据集的角度来看,输入的数量:50k 个训练数字图像,每个都单独表示为 784×1 的实数矩阵,该矩阵在 0 到 1 之间,是像素的灰度值
- 从 MNIST 数据集的角度来看,输出:经过我们的神经网络后,是数字输出-10 的数组-代表 0 到 9 之间的每个数字-其值也在 0 到 1 之间,这是特定数字的概率值
输出数量(对于给定的图像输入)= 10 个输出
- 从 MNIST 数据集的角度来看,原始输出:给定图像的经过注释和验证的数值-我们可以将其转换为 10 个值的数组,每个值表示数字 0 到 9,其中对应的数字为 1,其余的数字为零
原始输出的数量(对于给定的图像输入-表示为每个数字为 10–1 的数组)= 10 个输出
- 从 MNIST 数据集的角度来看,输入/样本计数:50K 个训练样本
由于成本是输出激活的函数,我们可以写为:
反向投影方程推导
以链式法则为例,我们可以说,重量/偏差的任何波动都会影响组合输入
这反过来影响产量
最后影响成本/误差
作为推论,输入的任何变化都会影响输出和成本,输出的任何变化也会影响成本,不管重量/偏差的变化,也就是说,每个中间组件都会影响它的邻居和通道中的所有其他组件。相反,最终成本的任何变化都依产出、投入、权重和偏差的顺序而定。将 w.r.t .枚举到第 l 层节点 j 中的上述符号:
变化和影响的可能组合
当发现成本的变化时,权重/偏差的变化产生 4 个重要的等式,包括 反向投影 算法— BP1…BP4。
成本与重量的变化
参照上图,让我们来算出第一个也是最重要的等式——成本变化与重量变化的关系
成本相对误差的变化
最后一层输入的成本变化
中间层输入的成本变化
我们可以使用全导数,但这很繁琐,我们将看到如何通过在单线多层网络上应用链式法则来推导,并将其用于多行多层网络,理解其背后的直觉,但现在等式如下:
- 向量形式:
- 基于网络中的每一行:
BP2 —直观推导—单行网络
下面让我们考虑一个线性网络,它有四个神经元,每个神经元都有权重、偏差、输入/激活和输出,如图所示。上标代表它们所在的层。
向前传递:激活
反向传递:优化重量以降低成本
根据梯度下降和链式法则,我们知道我们需要计算导数,从而计算“w”和“b”中的变化,以使成本最小,即——回想一下,w 中的任何波动都会影响通道中的所有中介,我们最初尝试找到最后一层的成本变化。对于单排神经元多层网络,更容易计算中间值。按链规则:
现在,我们可以总结重量的“反向传递”,类似地,您也可以测试偏差(鼓励读者进行详细的测试,为了简洁起见,省略了测试),如下所示:
BP2 —直观推导— 2 排 4 层网络
让我们关注第 4 层,根据我们之前的理解,我们可以计算出如下激活:
计算从第 4 层到第 3 层的反向通路,总结如下:
反向投影—一种不同的拍摄方式
如上图所示,激活 w.r.t 第 3 层(从 3 到 4): 只不过是第 4 层权重乘以第 3 层输出并加到第 4 层偏置上的和,反馈给激活函数,给出第 4 层的输出,即
而反向传播 w.r.t 第 3 层(从 4 到 3): 是第 4 层权重转置乘以第 4 层成本变化 w.r.t 输出第 3 层激活差异的 hadmard 乘积。下图对此进行了说明。
裸神经网络的内部工作——与代码匹配的矩阵
我们神经网络上的 MNIST 数据集—节点和层
类似于“hello world”程序对任何语言都是事实,MNIST 数据集是神经网络学习的事实。它包含 60K 训练图像和 10K 测试图像。这些图像被标准化以适合 28×28 像素的分辨率,并进行抗锯齿处理以给出灰度。我们将继续构建一个神经网络,其:
Generated with NN-SVG Tool
- 第一层——输入层:
将是一个 784 节点——以线性 784×1 阵列矩阵的 28×28 像素边界框图像为特征 - 第二层—隐藏层
3.第三层—隐藏层
将是 40 个节点的隐藏层—权重矩阵将是(40×100),偏差矩阵将是(40×1)
4.第四/最后一层—输出层
将是 10 个节点的输出(伪隐藏)层—权重矩阵将是(10×40),偏差矩阵将是(10×1)。如此选择最后一层,使得每一层将代表一个数字(数字 0…9 中的任一个。因为该值是实数,所以输出表示该特定数值的概率,比如说大于 0.5 是肯定的,小于 0.5 是不可能的。
我们将使用上述神经网络架构来理解迈克尔·尼尔森的书中给出的 python 代码。这在 python 中提供了一个紧凑、简单的最小神经网络实现。
- 将下面的 repo 克隆/复制到本地目录
https://github . com/mnielsen/neural-networks-and-deep-learning . git - 移动到您克隆的本地目录,并从这里启动 python 2.6 或 2.7
- 运行以下代码片段开始:
Python 中的整体 NeuralNet
查看 network.py 的源代码,并按照下面提到的函数进行操作,以便更好地理解 python 逻辑的功能!
- SGD 函数
(循环遍历次数)
a .参数:
training_data —相应格式化的训练数据矩阵
epochs —对整个数据集进行迭代的次数
mini_batch_size —顾名思义
eta —学习率乘数
test_data —相应格式化的测试数据矩阵—用于评估学习到的权重和偏差
b .混洗所有 50K 个训练记录(每个包含 784 - update_mini_batch 函数
(循环每个小批量计数)其中小批量计数=总训练样本除以小批量大小
a .将权重和偏差矩阵初始化为零
a .执行反向传播(包括前馈和反向传播)。循环每个训练记录+其小批量输出记录
c .获取增量矩阵并添加到之前的增量矩阵(针对权重和偏差)
d .计算 GD 并从权重和偏差中减去 - 反向传播函数 a .执行前馈——围绕每层权重/偏差循环计算每层的输入&激活矩阵
b .执行反向传递
c .首先计算最后一层增量
d .从倒数第二层循环到第一层&计算每层权重和偏差的增量矩阵
前馈和反向反馈-用 Python 计算的矩阵
4。评估函数 a .使用 SGD 计算的权重和偏差,对训练输入
执行前馈 b .获取最终输出并将其与实际输出
c .报告比较的正确结果计数
希望它对你的深度学习之旅有所帮助,如果你想联系,请给我发电子邮件
学习曲线回溯—参考文献和致谢
这些是我阅读、思考和吸收的书籍、博客和代码,因为我试图理解深度学习,并希望通过在这里陈述它,可能有助于在你的旅程中引用它们,同时感谢所有类似地帮助打造深度学习的人。
书籍
代码
激活功能
损失函数
梯度下降
反向传播
原载于 2019 年 4 月 29 日http://avantlive.wordpress.com*。*
故障检测,即预测健康管理和测试方法
In most cases, machines can’t tell you when they’re sick
机器什么时候会出故障
“预测健康管理(PHM)是对系统状态的询问,并使用潜在损坏的非破坏性评估来评估已部署系统中的产品寿命。系统健康状况通常在实际操作环境中进行评估。”
(http://cave . auburn . edu/RS rch-thrusts/proportional-health-management-for-electronics . html)。
这就是 PHM 的定义,至少谷歌是这么认为的。有人称之为“故障检测”,有一段时间我确实这么叫它,直到我了解到它不仅仅是能够检测故障何时发生。它还根据收集的数据预测机器的剩余寿命。
这是一个有趣的话题,考虑到几乎每一个制造的物品或它被制造的物品都受益于知道它什么时候会出问题。随着我的兴趣被激起,我对方法做了一些研究,并在我能找到的几个数据集上进行了一些实验(由于专有信息,很难找到这样的数据)。有了这个,我想采取一些方法。首先,进入直接故障分类,然后预测剩余使用寿命(RUL)。
我还应该指出,PHM 的核心也是时间序列分析。
故障分类
一言以蔽之,故障分类基本上是一个二元分类问题。这种观察有错还是没有错?为了节省您的时间,我将简单介绍一下这个模型,给出一个简单的结果,并在最后贴上一个 Github 的链接。
我为这个特定模型选择的模型看起来像这样:
Fault Classification Model
只是为了突出可能不太明显的项目:
滤镜
我用来预处理数据的滤波器是卡尔曼滤波器。有许多论文详细介绍了所涉及的数学,但总而言之,它有助于消除传感器数据中的“噪声”。在自动驾驶汽车传感器和其他一些应用中,它被大量用于接收信息。这里有一篇关于卡尔曼滤波器的中型文章 https://Medium . com/@ jae ms 33/understanding-Kalman-filters-with-python-2310 e 87 b8 f 48,Github repo 用 python 对其进行了编码。
上/下采样
在这种特殊情况下,不到 1%的数据实际上是错误的。像这样的问题,SMOTE 自 imblearn 库https://unbalanced-learn . readthedocs . io/en/stable/generated/imb learn . over _ sampling。SMOTE.html将从少数民族标签中随机生成新样本。
结果如下:
Precision: .99 , Recall: .99, F1: .99, CV: 95.4
剩余使用寿命
这一部分提出了更大的挑战。RUL 根据以前周期的数据预测机器的寿命。为此收集的数据来自 PHM 协会 2008 年的一次实际比赛。同样,查看 Github 来查看数据和完整的笔记本。
我为这个模型选择了一条更复杂的路线:
Remaining Useful Life Model
以下是不太明显的步骤:
目标工程
这个特殊的数据集没有现成的训练目标。所以我做了一个:
- 利用可用的周期
- 对每个单元进行反向排序
- 转换成剩余寿命的百分比(重要的一步)
xgb 回归器/特征工程 g
这两个为什么在一起?基本上,这是一个模型堆叠的例子。对于像这样的回归问题,分类数据可能是一个小烦恼。通过训练好的回归网络运行分类数据并使用这些输出作为另一个特征是有意义的。最终,它确实在损失改善方面发挥了相当大的作用。我测试了一个调优的 XGBRegressor 和一个深度神经网络。令人惊讶的是,两者的结果非常接近。
Unit 1 results — test set RMSE between 49–55
两者的结果因单位而异,有些单位的 NN 优于 XGB,反之亦然。
外卖
如果从这篇文章中有所收获的话,那就是我们发现的这些收获:
- 两个数据集都没有关于所采取措施的细节——理论上,不同类型的测量(压力、温度、振动)会有不同类型的方法,适用于适当的预处理。
- 仅仅从视觉上,你可以看到神经网络是如何学习的。最初的几次迭代偏离很远,随着它的进展,它逐渐接近目标。
- 这个领域的机会是无限的。在给定信息的情况下,这两个模型的具体程度,可以肯定地说,任何机器和/或材料在某个时候都会有一个为其设计的模型。
关于这个主题有大量的研究,也有许多方法。我采用的方法可能不是最好的方法,我会继续研究这个课题。欢迎所有评论。感谢您的宝贵时间!
Github 位于:https://github . com/Jason-M-Richards/Fault-Detection-Methodologies
恐惧告诉我们必须做什么
我 2019 年 9 月 30 日至 2019 年 7 月 10 日的深度学习自学
我是一名数学讲师,也是一名有抱负的数据科学家,希望参与人工通用智能研究,本周我决定开始每周写一篇博客,记录我一直在做的事情,既是为了自己参考,也可能是为了帮助走上类似道路的其他人,遵循 fast.ai 的雷切尔·托马斯的建议,“写一篇能帮助你在六个月前阅读的博客。”
我有一个纯数学的硕士学位,但我没有太多的统计知识,所以我正在通过可汗学院的“统计和概率”课程学习,我还通过 fast.ai 的“程序员实用深度学习”学习深度学习。最后,我正在使用 Udemy 上何塞·波尔蒂利亚的“数据科学和机器学习的 Python boot camp”学习 NumPy、Pandas、matplotlib 和其他数据科学的 Python 库。
在过去的几周里,我倾向于只关注其中的一个,直到我陷入困境,然后跳槽去做另外一个。对一些人来说,这种工作流程可能是高效的,但我可以感觉到自己在使用它来避免在处理困难的事情时冒出的自我怀疑的情绪,我最后提醒自己这句话:
~Steven Pressfield, The War of Art: Break Through the Blocks & Win Your Inner Creative Battles (https://stevenpressfield.com/books/the-war-of-art/)
所以上周,我为每周的每门课程设定了具体的学习目标。我认为避免我在自己身上观察到的两种次优学习行为会更容易:完全避免困难的事情,而选择简单的事情,或者——在有用性钟形曲线的另一端——在困难的事情上花费太多时间,在其他事情上毫无进展。用机器学习的术语来说,我需要把我自己的学习速率调得更高,以避免陷入局部最小值!
每周,我计划做:
一堂来自 fast.ai 的课
一个单位的统计数据
Python 课程的 0.5–1 个部分(一些部分是简短的,其他部分是完整的项目)
阅读一篇深度或机器学习论文
当我真的不想做某件事时,我会使用番茄工作法:我只要求自己做 25 分钟,然后我可以停下来或者休息一下。前 25 分钟几乎总能让我克服情绪上的阻力,之后我会更容易坚持下去。这个技巧也帮我完成了研究生期间的家庭作业!
所以上周,我做了 fast.ai 的“程序员实用深度学习”第四课,你可以在这里详细阅读我的经验。
我还学习了可汗学院统计学课程的“探索双变量数据”和“研究设计”单元,学习如何计算和解释最小二乘回归线,以及实验研究和观察研究。
在 Udemy 上的“Python for Data Science and Machine Learning boot camp”课程中,我完成了第 5-7 节,复习了我在 Coursera 上的 Deep Learning . ai“Deep Learning”课程中学到的 NumPy 语法,并练习了用 Pandas 查询数据。我以前学过一些 SQL,但没有意识到 Pandas 和 SQL 有多少共同点。然后我开始想知道什么时候一个比另一个更好用,并发现这个帖子非常有帮助:
使用哪种工具取决于你的数据在哪里,你想用它做什么,你自己的优势是什么。如果您的数据已经是一种文件格式,就没有必要使用 SQL 做任何事情。如果您的数据来自数据库,那么您应该通过以下问题来了解您应该在多大程度上使用 SQL。
~《SQL 与熊猫》(https://towardsdatascience.com/sql-and-pandas-268f634a4f5d)作者凯丽·史密斯
我选择阅读的论文——“用好奇心驱动的深度强化学习学习温和的物体操纵”——来自 DeepMind ,作者是 Sandy H. Huang、Martina Zambelli、Jackie Kay、Murilo F. Martins、Yuval Tassa、Patrick M. Pilarski 和 Raia Hadsell。你可以在这里 查看我做的笔记和我阅读时的疑问.)。这项工作围绕着使用深度强化学习来教机器人轻轻地处理物体。
这是我第一次阅读深度或机器学习论文,事实证明这是一个非常棒的选择。即使对于外行人来说,整体想法也是直观的,更好的是,实际实验的模拟和视频使得方法和结果易于理解。但是当我阅读的时候,我仍然有很多问题,并且学到了很多。
在这篇论文之前,我没有读过多少关于强化学习的东西,我不明白它会有什么帮助。奖励?处罚?为什么?为什么都是?我不明白为什么一个机器人执行一项特殊的任务需要“奖励”,更不用说“痛苦惩罚”了。现在看来很明显,我明白了,但这确实是该项目的“学习”方面:如果我们提前知道机器人应该使用多少力来处理一个物体,我们就完成了。但是,如果我们希望能够让机器人处理一个它以前从未遇到过的物体,而不必为它采取的每个动作指定一个力的大小,我们必须有一种方法让它知道它什么时候做对了。
当学习者的一个实例(“代理”)被训练与一个物体接触时只有奖励,它会经常过度用力。然而,只对过度用力进行惩罚的训练,会导致代理人完全避免与物体接触。受儿童发展研究的启发,作者为一个代理人设立了一个奖励,当它预测到它应该不正确地使用的力量时,以真正的金发女孩的方式,三种奖励类型的组合是赢家。代理人对疼痛惩罚感到“好奇”,并试图探索不同的力量来找到“恰到好处”的力量。
很难表达这篇论文读起来有多有趣,以及瞥见人类心理学如何被用来推进人工智能是多么令人着迷。我们称之为人工“智能”,因为我们明确理解这种体贴是人类的努力,但令人难以置信的是,它是如何实际实现的。
我是加州大学东湾分校的数学讲师,也是一名有抱负的数据科学家。在 LinkedIn 上和我联系,或者在 Twitter 上和我打招呼。
使用 SVM 权重的特征消除
特别是对于 SVMLight,但是这种特征消除方法可以用于任何线性 SVM。
Figure 1: a random example of accuracy based on the number of SVM features used.
大约在 2005 年至 2007 年,我在写硕士论文时,不得不根据 SVM 模型计算特征权重。这是在 2007 年开始的 SKlearn 之前。这个想法是基于算法认为影响最小的特征来迭代地删除冗余特征,看看我们可以在不牺牲性能的情况下删除多少。今天,您可以在 SKlearn 和其他包中轻松地选择这些类型的特性,但是,如果您坚持使用 SVMLight,您可以使用下面的代码。总的来说,这种方法今天仍然有效。
当时我们有两个选择,SVMLight 或者 LibSVM。我选择了 SMLight。Thorsten Joachims 发布了一个 Perl 脚本来计算 SVM 重量,但我使用的是 python,我用 Python 重写了他的脚本,他还在他的网站上放了一个下载链接。
可以在这里找到 Perl 脚本原文:http://www.cs.cornell.edu/people/tj/svm_light/svm_light_faq.html 和 Python 脚本:http://www . cs . Cornell . edu/people/TJ/SVM _ light/SVM 2 weight . py . txt
使用该脚本将获得所有要素的权重。这在以后非常有用,正如您在下面的伪代码中看到的,您可以系统地消除功能:
- K = 50%
- 在对所有当前特征进行训练之后,选择具有最高 SVM 权重 s 的 K 个特征和具有最低(最负)SVM 权重的 K 个特征
- 再培训
- 基于看不见的数据集测量准确性
- 迭代:转到 2。当没有更多功能可供选择时停止。
- 选择如图 1 所示的最佳“弯头”。在本例中,重点是 128 个特征允许您获得与所有特征相同的精度。
您会注意到,仅使用您的特征子集就可以获得更高的预测结果。这是特征选择的本质。
# Compute the weight vector of linear SVM based on the model file
# Original Perl Author: Thorsten Joachims (thorsten@joachims.org)
# Python Version: Dr. Ori Cohen (orioric@gmail.com)
# Call: python svm2weights.py svm_modelimport sys
from operator import itemgettertry:
import psyco
psyco.full()
except ImportError:
print 'Psyco not installed, the program will just run slower'def sortbyvalue(d,reverse=True):
''' proposed in PEP 265, using the itemgetter this function sorts a dictionary'''
return sorted(d.iteritems(), key=itemgetter(1), reverse=True)def sortbykey(d,reverse=True):
''' proposed in PEP 265, using the itemgetter this function sorts a dictionary'''
return sorted(d.iteritems(), key=itemgetter(0), reverse=False)def get_file():
"""
Tries to extract a filename from the command line. If none is present, it
assumes file to be svm_model (default svmLight output). If the file
exists, it returns it, otherwise it prints an error message and ends
execution.
"""
# Get the name of the data file and load it into
if len(sys.argv) < 2:
# assume file to be svm_model (default svmLight output)
print "Assuming file as svm_model"
filename = 'svm_model'
#filename = sys.stdin.readline().strip()
else:
filename = sys.argv[1] try:
f = open(filename, "r")
except IOError:
print "Error: The file '%s' was not found on this system." % filename
sys.exit(0) return fif __name__ == "__main__":
f = get_file()
i=0
lines = f.readlines()
printOutput = True
w = {}
for line in lines:
if i>10:
features = line[:line.find('#')-1]
comments = line[line.find('#'):]
alpha = features[:features.find(' ')]
feat = features[features.find(' ')+1:]
for p in feat.split(' '): # Changed the code here.
a,v = p.split(':')
if not (int(a) in w):
w[int(a)] = 0
for p in feat.split(' '):
a,v = p.split(':')
w[int(a)] +=float(alpha)*float(v)
elif i==1:
if line.find('0')==-1:
print 'Not linear Kernel!\n'
printOutput = False
break
elif i==10:
if line.find('threshold b')==-1:
print "Parsing error!\n"
printOutput = False
break i+=1
f.close() #if you need to sort the features by value and not by feature ID then use this line intead:
#ws = sortbyvalue(w) ws = sortbykey(w)
if printOutput == True:
for (i,j) in ws:
print i,':',j
i+=1
Ori Cohen 博士拥有计算机科学博士学位,专注于机器学习。他领导着 Zencity.io 的研究团队,试图积极影响市民的生活。
泰坦尼克号数据集的特征工程和算法精度
机器学习最流行的数据集之一对应于泰坦尼克号事故
在这里,我们正在处理这个数据集中的特征,试图发现选择不同的特征对一些基本的最大似然算法的准确性的影响。这些特征对应于数据的头部
the head of the dataset (first 5 rows)
数据集显示了一名乘客是否幸存(1)以及在几列中对该乘客的描述,如他的阶级(Pclass)、年龄等。下表(从 kaggle 中提取)对这些特性进行了描述:
我们做的第一个图形探索性分析显示了特征之间的一些关系
我们可以为一些特殊的功能关闭它,例如 SibSp 和 Parch
这幅图描述了一个乘客是否幸存,基于他/她同行的家庭成员的数量。不过,他的关系并不清楚。
我们建议在随机森林分类器下传递这些特征,以更好地理解它们对存活率的影响:
结果并不清楚,这就是为什么我们创造了新的功能,如“孤独”或“家庭大小”。
下一个图显示了这些特征对幸存乘客数量的影响:
同样,我们可以探讨年龄和阶级之间的关系:
然而,当我们再次训练一个**随机森林算法时,这种关系更加清晰。**下一个图显示了基于乘客年龄和阶级的存活率
Before grouping Age feature
这很好,但是,为年龄创建更小的组,算法显示出更好的洞察力:
After grouping Age feature
这种表示更清楚,因此我们期望在年龄列中使用较小的组(而不是连续值)的学习算法会有更好的结果
此外,我们还可以检查新功能是否为我们未来的模式增加了价值:
这不是很清楚,所以我们也可以评估所有特征的熵,并删除这些可能产生比价值更多的噪声的列:
Survived column entropy : 0.96
Pclass column entropy : 1.44
Age column entropy : 1.4
SibSp column entropy : 1.34
Parch column entropy : 1.13
Fare column entropy : 1.61
FamilySize column entropy : 1.82
IsAlone column entropy : 0.97
Age*Class column entropy : 2.14
female column entropy : 0.94
male column entropy : 0.94
C column entropy : 0.7
Q column entropy : 0.42
S column entropy : 0.85
训练模特
我们测试了 4 个选项,第一个是在功能没有任何变化的情况下进行的训练(即,没有上述部分的工作):
No features, we use only the columns of the original data
下一次尝试包括了第一部分中创建的所有功能:
accuracy of model with all features
接下来,我们只包括那些用熵< 2, that is droping those columns that maybe add more noise than value
Feature with entropy < 2
it seems that the global accuracy of all the models is increasing
Next, we select only those features with entropy < 1,5 :
Feature with entropy < 1,5
It seems this feature combination give the better accuracy for all the algorithms
训练 KNN 的特征
另一点,例如,当训练一个有几个邻居的 KNN 时,结果取决于定义的特征。
我们绘制了几个邻居的 KNN 的精确度:
该图清楚地显示了定义要素将如何改变算法的输出,甚至是在选择其参数之前(例如,KNN 的邻域数)
这个“实验的代码在这里:
使用泰坦尼克号的数据:灾难中的机器学习
www.kaggle.com](https://www.kaggle.com/felipesanchezgarzon/eda-feature-engineering-comparison-ml-accuracy)
感谢阅读!!!
如果你想继续阅读这样的故事,你可以在这里订阅!
使用超市销售数据的特征工程和数据准备
在上一篇 文章 中,我们对超市销售数据集进行了基本的数据探索。我们将在这一部分使用这些重要的见解和发现。我建议你在这里查看第一篇文章,这样我们就在同一页上了。
在开始之前,让我们了解什么是特征工程,以及我们为什么要这样做。
我相信我应该首先定义什么是特性,所以开始吧;
**特征仅仅是原始数据的一个方面的表示,一些作者也称之为属性。例如, **Product_Weight、Product_Type、和 Product_Price 是我们超市数据中的一些特征。这些特征可以是数字或分类的,最后必须转换成数字格式(机器学习模型可以使用的唯一类型)。
现在,让我们定义特征工程;
特征工程是从原始数据中提取重要特征并将其转换为适合机器学习模型的格式的行为。为了进行功能工程,数据科学家必须使用领域知识(关于特定领域的知识)、数学和编程技能来转换或提出新的功能,以帮助机器学习模型更好地执行。
特征工程是机器学习的一个非常广泛和实用的领域,我建议你查阅一些关于这个主题的书籍或课程,因为这篇文章只是触及了表面。下面列出了一些链接。
有了这些基本知识,让我们开始研究超市数据。在这篇文章的最后,我们将有一个干净的数据集,我们可以扔进任何机器学习模型。
首先,让我们实现上一篇文章(数据探索)中的所有发现。
新特征创建
code for is_normal_fat feature creation
代码解释:
代码片段 1:我们首先打印出 Product_Fat_Content 列中唯一的类,这样我们就知道正确的类别及其拼写。
代码片段 2:我们创建一个字典,并将类别映射到二进制类。
低脂肪和超低脂肪为 0 级,而正常脂肪为 1 级。
其次,我们使用 pandas map 函数将 Product_Fat_Content 中的值映射到我们创建的字典中的类。
代码片段 3:我们确认我们有类(0 和 1)
代码片段 4:打印出两个类中的点数。
我们建议创建的第二个功能是一个列,该列捕获具有(低价)正常脂肪的产品和具有(高价)低/超低脂肪的产品的价格。
现在,再次将正常脂肪从其他两类中分离出来会给我们一个多余的特征,因为我们已经在之前的特征(is_normal_fat)中将它们分离出来了。所以我们跳过这个特征创建。****
第三个被提议的特性是一个捕获开业年份的列。此功能将 Ssuper market _ Opening _ Year分为 90 年代和 2000 年代。**
我们创建这个列,如下所示。
代码片段 1:一个简单的函数,将年份作为参数,如果小于 1996,则返回 0,否则返回 1。
代码片段 2:我们使用 pandas apply 方法创建特性 open_in_the_2000s 。我们将函数(cluster_open_year)应用于超市 _ 开业 _ 年份中的每个值。
3:新列的预览。
第四个建议的特性是将产品类型分为两类。类别 1 将包含家庭、健康卫生、和其他类别,而类别 0 将包含其余类别。
让我们在下面创建它。
代码片段 1:这里我们获得了 Product_Type 列中的唯一类别,然后删除了属于类别 1 的类别。
代码片段 2:我们创建了一个简单的函数,如果 Product_Type 是类 0,则返回 0,否则返回 1。
代码片段 3:我们应用我们的函数来创建新列 Product_type_cluster。
转换倾斜特征
在我们的 EDA 过程中,我们发现产品 _ 超市 _ 销售和产品 _ 货架 _ 可见性是右偏的,我们建议将它们转换成正态分布。
代码片段 1:我们创建了一个 1 行 2 列的子图来保存转换前后的图,这样我们就可以比较变化了。
代码片段 2:在轴 1 中绘制普通的 Product_Supermarket_Sales 特性。
代码片段 3:使用 numpy 的 np.log1p( ) 函数对特征进行日志转换。该函数在应用对数函数之前将变量加 1。然后我们在第二个轴上绘制新的变换特征。
代码片段 4:用 tight_layout()参数在两个轴之间添加空格,并添加一个标题。
当我们运行上面的代码时,我们注意到对数转换使特征Product _ Supermarket _ Sales更接近正态分布,这是更好的,并且主要是由统计学家鼓励的。如果想知道为什么统计学家喜欢正态分布,请点击这里。
接下来,让我们转换产品 _ 货架 _ 可见性
代码与前者非常相似,但是这里我们转换了 Product_Shelf_Visibility。
运行代码,我们得到以下结果…
当我们应用对数变换时,这个特性没有显著的变化。这是因为这些值大多接近于零。像这样转换特性的高级方法是存在的,但是在这篇文章中我们将坚持使用它。
处理分类特征
接下来,我们将处理所有的分类列,但是首先,让我们解释一些重要的概念,它们将帮助我们正确地处理这些特性。
分类编码,是什么?
分类变量用于表示类别或类。例如,我们数据集中的 Product_Type 列是一个分类变量,有 3 个类别(低脂肪、超低脂肪和正常脂肪)。
分类编码是用于将非数字特征/变量转换成数字的方法。执行分类编码有多种方法,但这可能取决于我们正在处理的分类变量的类型。
在进行编码之前,我们通常考虑的两种范畴类型是序数和非序数/名词性范畴。在选择编码方案之前,理解这两种类型之间的区别是很重要的。我们先来了解一下他们。
序数范畴:这些是可以按数量顺序排列的范畴。也就是说,我们可以以这样一种方式安排这些类,使得一个类可以比另一个类具有更大的幅度。例如,我们数据集中的特征 SuperMarket_Size 是一个有序分类,因为分类标签 High 大于 Medium ,后者又大于 Small 。因此,如果我们对这些特征进行编码,我们肯定应该给高类分配一个更大的类号。
{高:3,中:2,小:1}
**这些类别在数量上没有有意义的差别。即一个类别标签不大于另一个。例如,在 feature Product_Type ,**,软饮料,不大于,家用,健康与卫生,,类也不大于,日记,类。
现在让我们来谈谈一些类型的编码
对于具有少量类别的分类特征的编码,通常少于 20 个
一热编码:在一热编码中,我们用一组比特来表示一个类,其中一个特定时间只能激活一个类。考虑我们数据集中的特征Supermarket _ Location _ Type,该特征有三个类(分类 1、分类 2 和分类 3)。我们可以使用如下所示的三位对该特性进行一次性编码。
在这里,我们可以看到我们使用了 3 位,一次只能激活一个类。
****哑元编码:哑元编码与单热编码相同,唯一的区别是在哑元编码中,我们比单热编码少用一位。也就是说,我们有一个全是 0 位的类。区别是微妙的,两种方法可以互换使用。
具有大量类别的分类特征的编码,通常大于 20 个
- ****标签编码:在标签编码中,我们简单的给每个类别分配整数,比如 1 到 n (n =类别数)。这通常会产生可订购的功能。
- ****注意:一些机器学习模型,如树或线性模型,可以很好地执行这种类型的编码,因为树不处理数据点的大小。但这将影响神经网络和基于距离的模型,如 KNN,它考虑了数据点的数量。
其他先进但有效的编码方案是嵌入、特征散列和容器计数。
现在,让我们看看我们的分类特征和它们的独特类别。
类别数最多的列是 Product_Type 列(15)。
根据我们对上述编码方案的理解,我们可以使用 one-hot-encoding 方案,因为我们的数据很小(大约 5000 个),唯一类别也很小。
对于 Python 中的 one-hot-encoding,我们将使用 pandas 中可用的虚拟函数。这是一个简单的方法,我们只需要调用。getdummies() 方法并传入我们想要编码的数据帧。****
处理缺失值
在清理和转换数据时,另一件重要的事情是处理丢失的值。这意味着我们可以删除丢失的值或者填充它们。
首先,我们将使用熊猫 isnull() 函数打印出每一列中缺失值的数量。
请注意,列的数量急剧增加,这是由于我们之前进行的一次性编码。一种热编码使用分类列的唯一类创建新要素。
从输出中,我们看到唯一缺少值的特性是 Product_weight 列。
在 4990 中有 802 个缺失值,我们将使用平均值来填充这些缺失值。
注意:除了仅使用平均值,还有其他填充或处理缺失值的方法。sci kit learn(python 中的一个机器学习包)有一个叫做的模块,用于自动处理缺失值。但是因为我们只有一列缺少值,所以我们将手动执行填充。
code for filling missing values
代码片段 1:我们使用 pandas mean()函数计算平均值。
代码片段 2:我们使用 pandasfillna方法用我们计算的平均值自动填充每个缺失值。 inplace = True 表示对原始数据集进行操作。**
最后,我们预览我们的变化。在这里,我们看到我们不再有丢失的值。
咻…我们的数据几乎准备好建模了。我们将执行最后两个重要的步骤:创建我们的系列和测试测试集和 f 特征标准化/缩放。
将我们的数据分为训练集和测试集
我们通常把数据分成三组。训练集、测试集和验证集。我们使用训练集来训练我们的模型,在每轮训练后使用测试集来评估我们的模型,并在项目结束时使用最终验证集来最终评估我们的模型。
最终的验证集很重要,因为在训练和测试的过程中,模型开始学习测试集,并且会不断地做得更好。
这是不好的,因为我们可能最终认为我们的模型会概括得很好,而实际上它只是记住了训练和测试集。因此,建议只在建模的最后使用验证集。
我们将使用 scikit-learn 中一个流行的函数 train_test_split 来拆分我们的数据。
code to split data into train and test sets
首先,我们导入函数 train_test_split,然后传递四个变量
- X_train:训练数据
- X_test:测试数据
- y_train:培训目标
- y_test:测试目标
在 train_test_split 函数中,我们传递想要分割的数据和目标,然后指定分配给测试数据的数据数量(0.3 = > 30%的数据)
最后,我们检查新数据的形状。
注意:X _ train 和 y_train 的第一个尺寸必须相同。
最后,我们将数据标准化
特征标准化/缩放
特征标准化/缩放用于将独立特征的值的范围标准化为大致相同的范围。这一点很重要,因为数据集的各个特征的值的范围变化很大,这可能导致大多数机器学习模型表现不佳。
例如,在我们的数据集中,特征超市 _ 开业 _ 年份的值的范围在 1992 年和 2016 年之间,而特征 Product_Shelf_Visibility 的值的范围以(0-0.4)为中心。这两个特性的取值范围非常不同,应该标准化。
当我们使用指定的方法进行规范化时,这些值被转换到相同的范围,有时在(0 和 1,-1 和 1 或最小值和最大值)之间
我们将使用robust scaler模块中的 Scikit learn 来执行本项目中的归一化。其他可用的归一化方法有最小最大缩放器、、规格化器、、标准缩放器、、最大最小缩放器、等等
首先,我们从 sklearn.preprocessing 导入 RobustScaler 包,然后我们从它创建一个对象。
其次,我们拟合我们的训练数据(X_train)。拟合过程是函数学习它将用于转换训练、测试和(如果可用的话)验证数据的所有度量的过程。
接下来,我们使用计算出的指标在 X_train 和 X_test 上执行实际的转换。
最后,我们打印新转换的数据的形状。
****注意:只适合你的训练数据!
咻…我们的数据终于可以用于建模了!
在这篇文章中,我们已经从探索性的数据分析转向实际的特征创建和清理。我们创建了新的要素,处理了缺失值,分割了数据,并执行了标准化。
我们在这里所做的一切都可以归类到数据准备之下,这是任何数据分析项目中最耗时的部分。
所以,去做一些功能创建和清理的乐趣。再见了。
本项目的笔记本和资料可以在 这里 找到
问题,评论和贡献总是受欢迎的。
在 twitter 上和我联系。
在insta gram上跟我连线。
Kaggle“房价竞赛”前 10 名的特征工程和集合模型
我们一步一步地详细说明开发回归模型的程序,以便在这场全球竞赛中进入“前 10 名”。
Photo by Jesse Roberts on Unsplash
aggle 是数据科学家和机器学习者最重要的在线社区之一。在 Kaggle 中,经常可以找到比赛,不同的用户生成预测模型并提交他们的结果,根据分数在排行榜中排名。
支持每个初学者进行的在线课程的竞赛是“Kaggle Learn 用户房价竞赛”。
因此,全球和以学习为导向的能力,网上流通的材料是丰富的,甚至对于那些看起来很好的人来说,可以直接找到优秀模型的完整代码,这些代码在记分牌上非常接近 1%。
本文基于 Kaggle 中 Sandeep Kumar 曝光的代码。这个代码是我们在比赛中最重要的起点。虽然单凭这段代码不足以进入前 10 名,但它使用了许多令人兴奋的技术。除了这里使用的方法之外,我们还添加了其他方法,使我们的模型进入了前 10 名。
因为当你开始尝试提高 1%以内时,每个细节都很重要,所以你需要非常有条理和彻底地处理数据工程和模型参数。
数据集和工作流
该数据集包含 1460 行房屋及其相应的 79 个要素和目标变量:每栋房屋的价格。还有一个测试集,有 1459 个其他的房子。
在本文中,我们将对我们将要应用的不同技术进行统一处理,并在实现最终预测以提高结果之前将它们分开。
数据集的工作流将
a)导入训练特征和测试特征数据。
b)保存并删除索引(Id ),准备不带目标变量的训练特征
c)创建连接它们的数据框“特征”
d)执行数据工程
e)在完成数据工程工作后,再次分割 X_train 和 X_test 数据集。
f)制作模型、拟合和预测
g)如果我们对目标变量(Y)应用任何变换,我们必须在预测上反转这个变换。
h)提交结果
目标变量:销售价格
我们正面临一个回归练习,我们必须计算每栋房子的销售价格,数据集的每一行。
我们的目标是列 SalePrice,首先,让我们看看它的一般行为:
train['SalePrice'].describe()
现在让我们看看它在任何直接使用它的模型中的实践,或者对它应用对数。
因此,我们看到,如果我们改变模型训练,将目标变量的对数而不是“销售价格”直接作为结果,那么从 12934 到 8458 的改进是显著的。
特征工程
与目标变量的相关性
相关性的目的是检查两个定量变量之间关联的方向和强度。因此,我们将知道它们之间关系的强度,以及当一个变量的值增加时,另一个变量的值是增加还是减少。”
如果你想知道更多关于变量和目标变量相关性的发生率,你可以回顾这一期的详细分析。
我们将创建一个包含数字列的数据框,并对它们应用 Pandas“corr()”方法,然后评估销售价格目标:
data_corr = train.select_dtypes(include=[np.number])
data_corr.head()
corr = data_corr.corr()
corr.head(20)
corr.sort_values(['SalePrice'], ascending=False, inplace=True)
corr['SalePrice']
这里我们有一个与目标变量相关的变量的有序列表,随着我们使用与目标变量越来越不相关的变量,模型精度的提高可能会被认为是降低的。
数据集中的异常值
“…异常值会显著影响估计统计数据的过程(,例如,样本的平均值和标准偏差),从而导致高估或低估值。因此,数据分析的结果在很大程度上取决于缺失值和异常值的 processed…"⁴方法
在引用这段话的文章中,可以深入找到关于异常值处理的优秀概念。
让我们以 GrLivArea 为例,它是与价格相关性最高的变量之一。
为了可视化异常值,我们通常使用箱线图。
这里有一篇关于箱线图和离群点 detection⁵的使用的优秀文章,以及他的主要特点:
- 箱线图有从 LQ 到 UQ 的方框,中间标有
- 它们描绘了数据最小值、LQ、中值、UQ、最大值的五个数字的图形摘要
- 帮助我们了解数据分布
- 帮助我们轻松识别异常值
- 25%的人口低于第一个四分位数,
- 75%的人口低于第三个四分位数
- 如果盒子被推到一边,而一些值离盒子很远,那么这是异常值的明显迹象
我们使用整个训练集对任何模型进行检查,剔除大于 4500 的异常值和大于 4000 的异常值,获得以下值
在 4000 或 4500 切割之间的性能差异似乎并不显著;然而,当 slice GrLivArea 寄存器大于 4500 时,精度的提高是相当可观的。
原则上,根据训练数据,这似乎是一个合适的策略。尽管如此,还是有必要在模型的入口中寻找一个平衡,因为它可能会产生过度拟合,或者使我们的模型失去泛化的可能性。
删除不必要的列并填充空值
如果我们使用此代码在这些操作之前和之后在数据框中部署空值,我们将获得如下图所示的图形。
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 6))
sns.heatmap(features.isnull())
plt.show()
请记住,在输入模型之前,必须清除所有的空值。
这些数据中的许多我们已经知道我们不打算精确化它们,或者填充空值的尝试对它们来说是不可行的;我们直接应用柱的下降。
features.drop(['Street'], axis=1, inplace=True)
features.drop(['Fence'], axis=1, inplace=True)
填充缺失数据的日常用法是用“无”或“零”来填充,这取决于数据的类型。
让我们在这里看两个例子来分配最频繁的值(Pandas mode 函数)或如何应用整个列或一组列的平均值。
这些示例仅用于说明目的;您可以应用任何 Pandas 函数,并根据您想要组成集群的任何属性进行分组。它还可以用于创建“集群”类型的新功能。
示例 a :应用最频繁的 MSZoning 值,为每个 MSSubClass 分组
features['MSZoning'] = features.groupby('MSSubClass')['MSZoning'].transform(lambda x: x.fillna(x.mode()[0]))
例 b :应用为每个街区分组的地段临街面的平均值
features['LotFrontage'] = features.groupby('Neighborhood')['LotFrontage'].transform(lambda x: x.fillna(x.median()))
例 c :用最频繁的量来完成你的特色
features['Exterior1st'] = features['Exterior1st'].fillna(features['Exterior1st'].mode()[0])
最后,我们必须确保没有空值,以便我们可以根据列的类型将填充应用于无或零:
objects = [col for col in features.columns if features[col].dtype == "object"]
features.update(features[objects].fillna('None'))numerics = [col for col in features.columns if features[col].dtype in np.number]
features.update(features[numerics].fillna(0))
根据他人创造变量
您可以创建新的变量来添加有助于模型定义数据集的特定信息,或者通过创建平均值、总和或布尔变量来简化模型。
必须仔细选择这些变量中的每一个,并且必须测量对模型的影响,以查看它是否增加了结果的质量,并且在用于“简化”的情况下,如果被消除的列的损失不影响模型的准确性。
features['CondTot'] = (features['Condition1'] + features['Condition2']) / 2features['OverTot'] = features['OverallQual'] + features['OverallCond']features['TotalSF'] = features['TotalBsmtSF'] + features['1stFlrSF'] + features['2ndFlrSF']features['haspool'] = features['PoolArea'].apply(lambda x: 1 if x > 0 else 0)
倾斜特征
对于正态分布的数据,偏斜度应该在 0 左右。对于单峰连续分布,偏度值> 0 意味着分布的右尾有更多的权重。函数 skewtest 可用于确定偏斜值是否足够接近 0,从统计角度来说。⁵
我们使用 scipy.boxcoxp1 来计算 1 + x 的 Box-Cox 变换。 计算的 Box-Cox 变换为:boxs p1p:
y = ((1+x)**lmbda - 1) / lmbda **if** lmbda != 0
log(1+x) **if** lmbda == 0Returns *nan* if x < -1\. Returns *-inf* if x == -1 and lmbda < 0.
为了在 boxscox 1 中获得输入的λ值,我们使用来自 Scipy 的 boxscox _ normmax(features[ind]),它为输入 data⁷计算最佳的 Box-Cox 变换参数
这种转换只能在具有所有正值的特征上进行;如果需要对具有负值的元素进行转换,请参阅参考 note⁸。
from scipy.stats import skew
from scipy.special import boxcox1p
from scipy.stats import boxcox_normmaxnumerics = [col for col in features.columns if features[col].dtype in np.number]skew_f = features[numerics].apply(lambda x: skew(x)).sort_values(ascending=False)highest_skew = skew_f[skew_f > 0.5]
highest_skew
skew_idx = highest_skew.index
for ind in skew_idx:
features[ind] = boxcox1p(features[ind], boxcox_normmax(features[ind))features[highest_skew.index].head()
创建与目标变量相关的特征
当创建与目标变量相关的变量时,有必要只在列车数据集上执行这些操作,然后尝试将它们带到测试数据集,因为在那里我们不知道目标变量“Y”的值。
这类变量使我们在竞争排行榜上有了显著的改善
例如,我们可以计算每平方米的价格,与每一个社区或每一类住房相关联,然后我们首先在列车组中算出它:
train['Sqr'] = train['SalePrice'] / train['LotArea']
train['Sqr'] = train.groupby('Neighborhood')['Sqr'].transform(lambda x: x.median())
然后我们用每个街区的价格编了一本字典:
d = {}
for indice_fila, x_train in train.iterrows():
d.update({x_train['Neighborhood']:x_train['Sqr']})
最后,我们在测试数据集中创建了特征
test['Sqr'] = 0.00
for indice, x_test in test.iterrows():
test.loc[test.index == indice ,'Sqr'] = d[x_test['Neighborhood']]
分类对象变量
“一种热门的编码方式是,将一个包含分类数据的列进行标签编码,然后将该列拆分为多列。数字被“1”和“0”代替,取决于哪一列有什么值……”⁹
对对象特征进行分类可能非常方便,关于使用哪些列对其应用 OHE 的讨论非常广泛,并且它们的局限性也很分散,因为列中的每个不同值都将被转换为模型矩阵输入的新维度。有时,创建一个对不同值进行分组以降低维数的新特征可能会很方便,例如,一组具有相似特征和价格的邻域。
对于某些数值列,我们可能需要对它们进行分类,为此,将类型更改为字符串很方便,因为最后,我们将对 type 对象的所有列应用 get_dummies。
features['MSSubClass'] = features['MSSubClass'].apply(str)
features['MoSold'] = features['MoSold'].astype(str)
final_features = pd.get_dummies(features).reset_index(drop=True)
虚拟变量陷阱:
“虚拟变量陷阱直接来自于对分类变量应用的一热编码……一热向量的大小等于分类列占用的唯一值的数量,并且每个这样的向量中正好包含一个‘1’。这将多重共线性纳入我们的数据集中……”⁰
换句话说,当执行转换时,添加的 OHE 特征集与目标变量对齐,因此通常会删除任何列。在我们的模型中,我们没有应用这种技术。
过度进食
Sandeep Kumar 的原始工作包括在特征工程结束时应用 get_dummies,这是一种寻找那些具有大量零值(高于 99.94%)的列的代码,这些值是 drop。
这种技术间接地减轻了分类变量中虚拟变量陷阱的影响,并且还避免了在模型中包括由于分类内容的影响而与试图提取主要特征并对其进行概括的模型无关的列。
overfit = []
for i in X.columns:
counts = X[i].value_counts()
zeros = counts.iloc[0]
if zeros / len(X) * 100 **> 99.94:** overfit.append(i)overfit = list(overfit)
**overfit.append('MSZoning_C (all)')** overfit
X = X.drop(overfit, axis=1).copy()
集合模型
回归算法在支持分类后的列方面没有问题,所以除了非常不方便的情况,删除它们似乎没有必要,甚至不是一个好主意。然而,这只是一种方法;在这篇文章的结尾,我们将看到解决这个问题的其他方法非常不同,而且一样有用,甚至更有用。
在这种情况下,没有必要降低模型的维数,因为使用不同算法的训练时间不是问题,并且强力似乎不是一个坏策略。
“集合模型”是什么意思?
“集合模型给了我们出色的表现,可以解决各种各样的问题。它们比其他类型的技术更容易训练,需要的数据更少,效果更好。在机器学习中,集成模型是标准。即使你没有使用它们,你的竞争对手也是… 集合模型由几个本身并不优秀的弱模型组成,被称为弱学习者。当您将它们结合在一起时,它们可以弥补彼此的不足,并提供比单独部署时更好的性能。”
模型度量
竞争指标是平均绝对误差(MAE)。
对于初始测量,RMSE 和 CV_RMSE 使用。
因为 CV_RMSE 是负的,要应用平方根,需要翻转它。
def rmsle(y, y_pred):
return np.sqrt(mean_squared_error(y, y_pred))def cv_rmse(model):
rmse = np.sqrt(-cross_val_score(model, X, y, scoring="neg_mean_squared_error", cv=kfolds))
return rmse
模特们
这是 Sandeep Kumar 使用的原始模型的一个示例,我们可以看到大量超参数的使用:
gbr = GradientBoostingRegressor(n_estimators=3000, learning_rate=0.05,max_depth=4, max_features='sqrt', min_samples_leaf=15, min_samples_split=10, loss='huber', random_state=42)
制作集合模型
在我们对 GBR 模型执行的相同操作中,我们可以对其他不同的回归模型执行该操作。然后在一个函数中,我们给它们分配一个总值的关联系数,这样比率加起来就是 1。
Sandeep Kumar 混合模型:
def blend_models_predict(X):
return (
(0.1 * elastic_model_full_data.predict(X)) + \
(0.1 * lasso_model_full_data.predict(X)) + \
(0.1 * ridge_model_full_data.predict(X)) + \
(0.1 * svr_model_full_data.predict(X)) + \
(0.1 * gbr_model_full_data.predict(X)) + \
(0.15 * xgb_model_full_data.predict(X)) + \
(0.1 * lgb_model_full_data.predict(X)) + \
(0.25 * stack_gen_model.predict(np.array(X))))
Mi 混合模型:
def blend_models_predict(X=X):
return (
(0.10 * elastic_model_full_data.predict(X)) +
(0.05 * lasso_model_full_data.predict(X)) +
(0.05 * ridge_model_full_data.predict(X)) +
(0.05 * svr_model_full_data.predict(X)) +
(0.20 * gbr_model_full_data.predict(X)) +
(0.20 * xgb_model_full_data.predict(X)) +
(0.10 * lgb_model_full_data.predict(X)) +
(0.25 * stack_gen_model.predict(np.array(X))))
我们能够使用其他模型,如线性回归、树形回归等。分配给每个模型的重要性取决于我们对测试数据集上的算法的信心,有时 GBR、XGB 或 LGB 等算法在拟合中工作得很好,但由于在测试集中过度拟合而失败。另一方面,像 SVR 或 RIDGE 这样的模型往往精度较低,但更稳定。
把所有这些放在一起,花了很多时间的努力
其他方法
当我们设法将我们的模型放入世界比赛的前 10 名,并知道我们的知识非常匮乏,渴望获得更多,但感觉处于平稳状态时,我联系了当时排名第三的 Adam su wik,他仍然在那里处于领先位置:
亚当非常友好地同意在一篇非常生动的帖子中分享他的模型的方法。
我们在这里抄录了这篇文章中一些最重要的段落:
“……这不是什么秘密知识,只是简单的逻辑思维(尽管该领域的专业知识也会很有帮助)。
根据模型调整数据—线性回归很可能是首选。不幸的是,大多数数据没有多大意义,可能会对结果产生不利影响。例如,质量——小房子和大别墅的优秀质量并不具有相同的价值——它应该被“优秀质量的平方英尺大小”所取代。而且在我看来,大部分数据应该是这样准备的——结合地块大小、房屋面积、地下室面积、公摊面积等。
组合数据/简化模型——这是我经常使用的方法。世界没有那么复杂。在这种情况下,对我来说 3 个特征就足够了——房子有多大?,多好啊?有多舒服?,也许是一些额外的设施,如游泳池、额外的车库等。…
丢弃数据—我也经常使用它。如果我看不到价值,或者认为信息包含在其他地方,或者如果我以某种方式设计了数据,我会放弃一切可能的东西。一些专业知识在这里会非常有用,但是仅仅使用常识,许多看起来有影响的数据实际上是次要的。没有人会在廉价的地基上建造豪宅,反之亦然…
聚类——并非总是相同的规则适用于整个群体,如果我能够隔离一大群相似的案例,我将为他们建立一个单独的模型。从生活经验来看——如果你看看每平方英尺的价格,你会注意到,对于较小的公寓来说,它肯定更大。
我可能还可以描述一些其他的事情,但这一切都归结为一件事——仔细分析所有的数据,并思考如何设计它们以适应您使用的模型。我相信一个原则:“垃圾进,垃圾出”。你提供什么质量的数据,你将得到什么质量的结果…
南的。不幸的是,答案如上——没有秘密——做任何看起来有意义的事情。我喜欢“0”,“没有”,我真的喜欢我自己的预测,我讨厌平均值,这就像承认缺乏任何想法,只是最小化假设错误的成本…
这是另一种方法,还有更多方法可以使用网络。在任何竞争中占 1%本身就是一个足够好的模式,也是我们走在正确道路上的标志。
摘要
我们已经看到,在他人分享的初步工作中开始一项工作是多么重要,在这种情况下,Sandeep Kumar 模型的迁移学习。谢谢他!
之前的迁移学习为我们提供了基础知识和基本知识,比新手从在线课程中获得的经验更加详尽和精炼。
然后,经过几个小时的努力和系统测试,我们看到了每种技术对不同变量的影响,以做出关于工程特性和模型集合的决策。
有一天,我有了一个想法,这个想法很重要,被分散了注意力,但仍然在思考,这个想法是根据一个给定的概念来计算平方英尺的值,达到前十名的满足感是一个不可思议的时刻。
这场卡格尔比赛的目的是学习
这篇文章旨在传播一些技巧,如果你像我几个月前一样在游戏中陷入困境,你可以使用这些技巧,希望能帮助你走上改善模型的正确道路,或者帮助你点亮那盏有所作为的小灯。
如果有什么地方可以做得更好,我将非常感谢您的评论和报告。
参考
Kaggle 是一个由数据科学家和机器学习者组成的在线社区,归谷歌有限责任公司所有。Kaggle 允许用户查找和发布数据集,在基于网络的数据科学环境中探索和构建模型,与其他数据科学家和机器学习工程师合作,并参加竞赛以解决数据科学挑战。Kaggle 通过提供机器学习竞赛起家,现在还提供公共数据平台、基于云的数据科学工作台和简短的人工智能教育。2017 年 3 月 8 日,谷歌宣布他们正在收购 Kaggle。
[2]【https://www.google.com/url?sa=t】T4&source = web&RCT = j&URL = https://personal . us . es/vararey/adatos 2/correlacion . pdf&ved = 2 ahukewiaytlhqnlahugllkghel 2 b 24 qfjamegqibxab&usg = aovvaw 1 mijaxh 5 f 0 qxixmpead 5j
[3] 回归模型中相关性与时间特征的关联
“…基本上有三种方法来处理数据集中的异常值。一种方法是去除异常值,作为修整数据集的手段。另一种方法包括替换异常值或通过异常值权重调整来减少异常值的影响。第三种方法是使用稳健技术来估计异常值。
修剪:在这种方法下,分析排除离群值的数据集。微调估计量(如均值)会减少数据中的方差,并导致基于低估或高估的偏差。假设异常值也是观察值,将它们从分析中排除会使这种方法不适用于异常值的处理。
Winsorization :这种方法包括修改离群值的权重,或者用期望值替换离群值的测试值。权重修改方法允许在不丢弃或替换离群值的情况下修改权重,从而限制离群值的影响。值修改方法允许在排除异常值的观测值中用最大或第二小的值替换异常值。
稳健估计方法:当总体分布的性质已知时,这种方法被认为是合适的,因为它产生对异常值稳健的估计量,并且估计量是一致的。近年来,许多研究提出了各种稳健估计的统计模型;然而,由于复杂的方法学问题,它们的应用进展缓慢。"
[5]https://docs . scipy . org/doc/scipy/reference/generated/scipy . stats . skew . html
[6]https://docs . scipy . org/doc/scipy/reference/generated/scipy . special . boxcox 1p . html
[8]https://www . ka ggle . com/rtatman/data-cleaning-challenge-scale-and-normalize-data
“你可以做的一件事是加上一个固定值,这个值刚好大于你的最小负值,使你的所有值都正常。另一种规范化方法是约翰逊变换,它可以处理负值。
[11]https://medium . com/@ ODSC/ensemble-models-demystified-c 871 D5 ee 7793
https://github.com/scikit-learn/scikit-learn/issues/2439
sk learn . cross _ validation . cross _ val _ score 返回的均方误差始终为负值。虽然这是一个经过设计的决策,以便该函数的输出可以用于给定一些超参数的最大化,但直接使用 cross_val_score 时会非常混乱。至少我问自己,一个平方的平均值怎么可能是负的,并认为 cross_val_score 没有正确工作或者没有使用提供的度量。只有在深入 sklearn 源代码之后,我才意识到这个标志被翻转了。
scorer.py 的 make_scorer 中提到了这种行为,但是 cross_val_score 中没有提到,我认为应该提到,因为否则会让人认为 cross_val_score 没有正常工作