TowardsDataScience-博客中文翻译-2020-一百二十一-
TowardsDataScience 博客中文翻译 2020(一百二十一)
从零开始理解逻辑回归— Kaggle 笔记本
通过自己实现来学习算法。
由作者创建
目录
1.目标
2.加载数据
3.从文本中提取特征
4.实施逻辑回归
- 4.1 概述
- 4.2 乙状结肠
- 4.3 成本函数
- 4.4 梯度下降
- 4.5 正规化
5.火车模型
6.测试我们的逻辑回归
7.用 Scikit 学习逻辑回归测试
让我们用 Python 导入所有必要的模块。
# regular expression operations
import re
# string operation
import string
# shuffle the list
from random import shuffle
# linear algebra
import numpy as np
# data processing
import pandas as pd
# NLP library
import nltk
# download twitter dataset
from nltk.corpus import twitter_samples
# module for stop words that come with NLTK
from nltk.corpus import stopwords
# module for stemming
from nltk.stem import PorterStemmer
# module for tokenizing strings
from nltk.tokenize import TweetTokenizer
# scikit model selection
from sklearn.model_selection import train_test_split
# smart progressor meter
from tqdm import tqdm
1.目标
这个内核的目标是使用 twitter 数据集从零开始实现用于情感分析的逻辑回归。我们将主要关注逻辑回归的构建模块。这个内核可以提供对 内部如何进行逻辑回归 的深入理解。使用 JupytertoMedium python 库将笔记本转换成中型文章。Kaggle 笔记本可从这里获得。
给定一条推文,如果它有正面情绪,它将被分类👍或者消极情绪👎。这对初学者和其他人都很有用。
2.加载数据
# Download the twitter sample data from NLTK repository
nltk.download('twitter_samples')
twitter_samples
包含 5000 条正面推文和 5000 条负面推文。总共有 10,000 条推文。- 我们每个班都有相同数量的数据样本。
- 这是一个平衡的数据集。
# read the positive and negative tweets
pos_tweets = twitter_samples.strings('positive_tweets.json')
neg_tweets = twitter_samples.strings('negative_tweets.json')
print(f"positive sentiment 👍 total samples {len(pos_tweets)} \nnegative sentiment 👎 total samples {len(neg_tweets)}")positive sentiment 👍 total samples 5000
negative sentiment 👎 total samples 5000# Let's have a look at the data
no_of_tweets = 3
print(f"Let's take a look at first {no_of_tweets} sample tweets:\n")
print("Example of Positive tweets:")
print('\n'.join(pos_tweets[:no_of_tweets]))
print("\nExample of Negative tweets:")
print('\n'.join(neg_tweets[:no_of_tweets]))Let's take a look at first 3 sample tweets:
输出:
Example of Positive tweets:
#FollowFriday @France_Inte @PKuchly57 @Milipol_Paris for being top engaged members in my community this week :)
@Lamb2ja Hey James! How odd :/ Please call our Contact Centre on 02392441234 and we will be able to assist you :) Many thanks!
@DespiteOfficial we had a listen last night :) As You Bleed is an amazing track. When are you in Scotland?!
Example of Negative tweets:
hopeless for tmr :(
Everything in the kids section of IKEA is so cute. Shame I'm nearly 19 in 2 months :(
@Hegelbon That heart sliding into the waste basket. :(
- 推文可能包含 URL、数字和特殊字符。因此,我们需要对文本进行预处理。
预处理文本
预处理是流水线中的重要步骤之一。它包括在建立机器学习模型之前清理和删除不必要的数据。
预处理步骤:
- 对字符串进行标记
- 将 tweet 转换成小写,并将 tweet 拆分成令牌(单词)
- 删除停用字词和标点符号
- 删除 twitter 平台上的常用词,如标签、转发标记、超链接、数字和电子邮件地址
- 堵塞物
- 这是把一个单词转换成它最普通形式的过程。它有助于减少我们的词汇量。例如,engage 这个词有不同的词干,
- 订婚
- 订婚的
- 订婚
让我们看看如何实现这一点。
# helper class for doing preprocessing
class Twitter_Preprocess():
def __init__(self):
# instantiate tokenizer class
self.tokenizer = TweetTokenizer(preserve_case=False, strip_handles=True,
reduce_len=True)
# get the english stopwords
self.stopwords_en = stopwords.words('english')
# get the english punctuation
self.punctuation_en = string.punctuation
# Instantiate stemmer object
self.stemmer = PorterStemmer()
def __remove_unwanted_characters__(self, tweet):
# remove retweet style text "RT"
tweet = re.sub(r'^RT[\s]+', '', tweet)
# remove hyperlinks
tweet = re.sub(r'https?:\/\/.*[\r\n]*', '', tweet)
# remove hashtags
tweet = re.sub(r'#', '', tweet)
#remove email address
tweet = re.sub('\S+@\S+', '', tweet)
# remove numbers
tweet = re.sub(r'\d+', '', tweet)
## return removed text
return tweet
def __tokenize_tweet__(self, tweet):
# tokenize tweets
return self.tokenizer.tokenize(tweet)
def __remove_stopwords__(self, tweet_tokens):
# remove stopwords
tweets_clean = []
for word in tweet_tokens:
if (word not in self.stopwords_en and # remove stopwords
word not in self.punctuation_en): # remove punctuation
tweets_clean.append(word)
return tweets_clean
def __text_stemming__(self,tweet_tokens):
# store the stemmed word
tweets_stem = []
for word in tweet_tokens:
# stemming word
stem_word = self.stemmer.stem(word)
tweets_stem.append(stem_word)
return tweets_stem
def preprocess(self, tweets):
tweets_processed = []
for _, tweet in tqdm(enumerate(tweets)):
# apply removing unwated characters and remove style of retweet, URL
tweet = self.__remove_unwanted_characters__(tweet)
# apply nltk tokenizer
/ tweet_tokens = self.__tokenize_tweet__(tweet)
# apply stop words removal
tweet_clean = self.__remove_stopwords__(tweet_tokens)
# apply stemmer
tweet_stems = self.__text_stemming__(tweet_clean)
tweets_processed.extend([tweet_stems])
return tweets_processed# initilize the text preprocessor class object
twitter_text_processor = Twitter_Preprocess()
# process the positive and negative tweets
processed_pos_tweets = twitter_text_processor.preprocess(pos_tweets)
processed_neg_tweets = twitter_text_processor.preprocess(neg_tweets)5000it [00:02, 2276.81it/s]
5000it [00:02, 2409.93it/s]
让我们看看预处理 tweets 后得到了什么输出。我们能够成功处理推文,这很好。
pos_tweets[:no_of_tweets], processed_pos_tweets[:no_of_tweets](['#FollowFriday @France_Inte @PKuchly57 @Milipol_Paris for being top engaged members in my community this week :)',
'@Lamb2ja Hey James! How odd :/ Please call our Contact Centre on 02392441234 and we will be able to assist you :) Many thanks!',
'@DespiteOfficial we had a listen last night :) As You Bleed is an amazing track. When are you in Scotland?!'],
[['followfriday', 'top', 'engag', 'member', 'commun', 'week', ':)'],
['hey',
'jame',
'odd',
':/',
'pleas',
'call',
'contact',
'centr',
'abl',
'assist',
':)',
'mani',
'thank'],
['listen', 'last', 'night', ':)', 'bleed', 'amaz', 'track', 'scotland']])
3.从文本中提取特征
- 给定文本,以这样一种方式表示
features (numeric values)
是非常重要的,这样我们就可以输入到模型中。
3.1 创建一个单词包(BOW)表示法
BOW 代表单词及其在每个类中的出现频率。我们将创建一个dict
来存储每个单词的positive
和negative
类的频率。让我们指出一条positive
推文是1
,而negative
推文是0
。dict
键是一个包含(word, y)
对的元组。word
是处理过的字,y
表示类的标签。dict 值代表类y
的frequency of the word
。
示例:#单词 bad 在 0(负)类中出现 45 次{(“bad”,0) : 32}
# word bad occurs 45 time in the 0 (negative) class
{("bad", 0) : 45}# BOW frequency represent the (word, y) and frequency of y class
def build_bow_dict(tweets, labels):
freq = {}
## create zip of tweets and labels
for tweet, label in list(zip(tweets, labels)):
for word in tweet:
freq[(word, label)] = freq.get((word, label), 0) + 1
return freq# create labels of the tweets
# 1 for positive labels and 0 for negative labels
labels = [1 for i in range(len(processed_pos_tweets))]
labels.extend([0 for i in range(len(processed_neg_tweets))])
# combine the positive and negative tweets
twitter_processed_corpus = processed_pos_tweets + processed_neg_tweets
# build Bog of words frequency
bow_word_frequency = build_bow_dict(twitter_processed_corpus, labels)
现在,我们有各种方法来表示 twitter 语料库的特征。一些基本而强大的技术是,
- 计数矢量器
- TF-IDF 功能
1.计数矢量器
计数矢量器指示稀疏矩阵,并且该值可以是单词的频率。在我们的语料库中,每一列都是唯一的标记。
稀疏矩阵的维数将是
*no of unique tokens in the corpus * no of sample tweets*
。
示例:corpus = [ 'This is the first document.', 'This document is the second document.', 'And this is the third one.', 'Is this the first document?', ]
并且 CountVectorizer 表示为
[[0 1 1 1 0 0 1 0 1] [0 2 0 1 0 1 1 0 1] [1 0 0 1 1 0 1 1 1] [0 1 1 1 0 0 1 0 1]]
2.TF-IDF(术语频率-逆文档频率)
TF-IDF 统计度量,用于评估单词与文档集合中的文档的相关程度。TF-IDF 的计算如下:
TF-IDF 方程
词频:词频 tf(t,d) ,最简单的选择就是使用一个词(词)在文档中的出现频率。逆文档频率: idf(t,D) 衡量单词提供多少信息,即它在所有文档中是常见还是罕见。它是包含该单词的文档的逆分数的对数标度。定义见维基。
3.2.为我们的模型提取简单特征
- 给定一个推文列表,我们将提取两个特征。
- 第一个特征是一条推文中正面词的数量。
- 第二个特征是推文中负面词的数量。
这看似简单,不是吗?也许是的。我们没有向稀疏矩阵表示我们的特征。将使用最简单的特征进行分析。
# extract feature for tweet
def extract_features(processed_tweet, bow_word_frequency):
# feature array
features = np.zeros((1,3))
# bias term added in the 0th index
features[0,0] = 1
# iterate processed_tweet
for word in processed_tweet:
# get the positive frequency of the word
features[0,1] = bow_word_frequency.get((word, 1), 0)
# get the negative frequency of the word
features[0,2] = bow_word_frequency.get((word, 0), 0)
return features
打乱语料库,将训练集和测试集分开。
# shuffle the positive and negative tweets
shuffle(processed_pos_tweets)
shuffle(processed_neg_tweets)
# create positive and negative labels
positive_tweet_label = [1 for i in processed_pos_tweets]
negative_tweet_label = [0 for i in processed_neg_tweets]
# create dataframe
tweet_df = pd.DataFrame(list(zip(twitter_processed_corpus, positive_tweet_label+negative_tweet_label)), columns=["processed_tweet", "label"])
3.3 训练和测试分割
让我们保留 80%的数据用于训练,20%的数据样本用于测试。
# train and test split
train_X_tweet, test_X_tweet, train_Y, test_Y = train_test_split(tweet_df["processed_tweet"], tweet_df["label"], test_size = 0.20, stratify=tweet_df["label"])
print(f"train_X_tweet {train_X_tweet.shape}, test_X_tweet {test_X_tweet.shape}, train_Y {train_Y.shape}, test_Y {test_Y.shape}")train_X_tweet (8000,), test_X_tweet (2000,), train_Y (8000,), test_Y (2000,)# train X feature dimension
train_X = np.zeros((len(train_X_tweet), 3))
for index, tweet in enumerate(train_X_tweet):
train_X[index, :] = extract_features(tweet, bow_word_frequency)
# test X feature dimension
test_X = np.zeros((len(test_X_tweet), 3))
for index, tweet in enumerate(test_X_tweet):
test_X[index, :] = extract_features(tweet, bow_word_frequency)
print(f"train_X {train_X.shape}, test_X {test_X.shape}")train_X (8000, 3), test_X (2000, 3)
输出:
train_X[0:5]array([[1.000e+00, 6.300e+02, 0.000e+00],
[1.000e+00, 6.930e+02, 0.000e+00],
[1.000e+00, 1.000e+00, 4.570e+03],
[1.000e+00, 1.000e+00, 4.570e+03],
[1.000e+00, 3.561e+03, 2.000e+00]])
看一看样本训练特征。
- 第 0 个索引是添加的偏差项。
- 第一个指标代表正词频
- 第二个指数代表负词频
4.实施逻辑回归
4.1 概述
现在,让我们看看逻辑回归是如何工作和实现的。
很多时候,当你听到逻辑回归时,你可能会想,这是一个回归问题。不,不是, Logistic 回归是一个分类问题,是一个非线性模型。
由作者创建
如上图所示,大多数最大似然算法有 4 个阶段,
第一步。初始化权重
- 随机权重已初始化
第二步。应用功能
- 计算乙状结肠
第三步。计算成本(算法的目标)
- 计算二元分类的对数损失
第四步。梯度下降
- 迭代更新权重,直到找到最小成本
逻辑回归采用线性回归,并将 sigmoid 应用于线性回归的输出。因此,它产生了每一类的概率,其总和为 1。
回归:一元线性回归方程如下:
单变量线性回归公式
- 注意,θ值是 权重
- x_0,x_1,x_2,… x_N 是输入特征
你可能会想到这个方程有多复杂。我们需要将在ith
位置的每个特征的所有权重相乘,然后求和。
好在线性代数带来了这个易操作的方程。没错,就是矩阵
*dot*
产品。您可以应用特征和权重的点积来找到 z 。
4.2 乙状结肠
- sigmoid 函数定义为:
它将输入“z”映射到一个介于 0 和 1 之间的值,因此它可以被视为一个概率。
def sigmoid(z):
# calculate the sigmoid of z
h = 1 / (1+ np.exp(-z))
return h
4.3 成本函数
逻辑回归中使用的成本函数是:
这就是二进制分类的测井损失。在逻辑回归中计算所有训练样本的对数损失的平均值,对所有训练样本的等式 3 修改如下:
- m 是训练样本的数量
- 是 与 训练实例的实际标签。
- 【h(z(\theta)^{(i)}】为 与 训练样本的模型预测。
单个训练示例的损失函数是,
损失函数
- 所有的 h 的值都在 0 到 1 之间,所以日志会是负数。这就是将系数-1 应用于两个损失项之和的原因。
- 当模型预测 1,(h(z(θ))= 1)且标签 y 也为 1 时,该训练示例的损失为 0。
- 同样,当模型预测为 0,(h(z(θ))= 0,而实际标签也为 0 时,该训练示例的损失为 0。
- 但当模型预测接近 1(h(z(θ))= 0.9999)且标号为 0 时,对数损失的第二项变成一个很大的负数,再乘以-1 的总因子,转换成正的损失值。1×(1 0)×log(1 0.9999)≈9.2 模型预测越接近 1,损耗越大。
4.4 梯度下降
梯度下降是一种用于**迭代更新权重θ以最小化目标函数(成本)的算法。我们需要迭代地更新权重,因为,**
在初始随机权重下,模型不会学到太多东西。为了改进预测,我们需要通过多次迭代从数据中学习,并相应地调整随机权重。
对于权重之一θ_ J的成本函数 J 的梯度是:
梯度函数
4.5 正规化
正则化是一种通过惩罚成本函数来解决机器学习算法中过拟合问题的技术。在成本函数中会有一个附加的惩罚项。有两种类型的正则化技术:
- 拉索(L1 范数)正则化
- 岭(L2 范数)正则化
拉索回归(L1)L1-范数损失函数也被称为最小绝对误差(LAE)。$λ∑ |w| \(是一个正则项。它是\)λ$正则化项与权的绝对和的乘积。较小的值表示较强的正则化。*
岭回归(L2)L2-范数损失函数也称为最小二乘误差(LSE)。$λ∑ (w) \(是一个正则项。它是\)λ$正则化项与权的平方和的乘积。较小的值表示较强的正则化。*
你会注意到,这有很大的不同。是的,它做得很好。主要的区别在于你在成本函数中加入了什么类型的正则项来最小化误差。
L2(岭)缩小所有系数相同的比例,但它不消除任何特征,而 L1(拉索)可以缩小一些系数为零,也执行特征选择。
在下面的代码中将添加 L2 正则化
*# implementation of gradient descent algorithm def gradientDescent(x, y, theta, alpha, num_iters, c): # get the number of samples in the training
m = x.shape[0]
for i in range(0, num_iters):
# find linear regression equation value, X and theta
z = np.dot(x, theta)
# get the sigmoid of z
h = sigmoid(z)
# calculate the cost function, log loss
#J = (-1/m) * (np.dot(y.T, np.log(h)) + np.dot((1 - y).T, np.log(1-h)))
# let's add L2 regularization
# c is L2 regularizer term
J = (-1/m) * ((np.dot(y.T, np.log(h)) + np.dot((1 - y).T, np.log(1-h))) + (c * np.sum(theta)))
# update the weights theta
theta = theta - (alpha / m) * np.dot((x.T), (h - y))
J = float(J)
return J, theta*
5.火车模型
让我们训练梯度下降函数来优化随机初始化的权重。在第 4 节中已经给出了简要的解释。
*# set the seed in numpy
np.random.seed(1)
# Apply gradient descent of logistic regression
# 0.1 as added L2 regularization term
J, theta = gradientDescent(train_X, np.array(train_Y).reshape(-1,1), np.zeros((3, 1)), 1e-7, 1000, 0.1)
print(f"The cost after training is {J:.8f}.")
print(f"The resulting vector of weights is {[round(t, 8) for t in np.squeeze(theta)]}")The cost after training is 0.22154867.
The resulting vector of weights is [2.18e-06, 0.00270863, -0.00177371]*
6.测试我们的逻辑回归
是时候在模型之前没有见过的测试数据上测试我们的逻辑回归函数了。
预测一条推文是正面的还是负面的。
- 将 sigmoid 应用于 logits 以获得预测值(介于 0 和 1 之间的值)。
新推文预测
*# predict for the features from learned theata values
def predict_tweet(x, theta):
# make the prediction for x with learned theta values
y_pred = sigmoid(np.dot(x, theta))
return y_pred# predict for the test sample with the learned weights for logistics regression
predicted_probs = predict_tweet(test_X, theta)
# assign the probability threshold to class
predicted_labels = np.where(predicted_probs > 0.5, 1, 0)
# calculate the accuracy
print(f"Own implementation of logistic regression accuracy is {len(predicted_labels[predicted_labels == np.array(test_Y).reshape(-1,1)]) / len(test_Y)*100:.2f}")Own implementation of logistic regression accuracy is 93.45*
到目前为止,我们已经看到了如何自己实现逻辑回归。得到了 94.45 的精度。让我们看看来自流行的机器学习(ML) Python 库的结果。
7.用 Scikit 学习逻辑回归测试
这里,我们将训练内置 Python 库中的逻辑回归来检查结果。
*# scikit learn logiticsregression and accuracy score metric
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
clf = LogisticRegression(random_state=42, penalty='l2')
clf.fit(train_X, np.array(train_Y).reshape(-1,1))
y_pred = clf.predict(test_X)/opt/conda/lib/python3.7/site-packages/sklearn/utils/validation.py:73: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
return f(**kwargs)print(f"Scikit learn logistic regression accuracy is {accuracy_score(test_Y , y_pred)*100:.2f}")Scikit learn logistic regression accuracy is 94.45*
太好了!!!。结果非常接近。
最后,我们自己实现了逻辑回归,并尝试使用内置的 Scikit learn 逻辑回归来获得类似的准确性。但是,这种特征提取的方法非常简单和直观。
我是边做边学。 欢迎在评论中留下您的想法或任何建议。非常感谢你的反馈,它能增强我的信心。
🙏感谢阅读!你可以通过 LinkedIn 联系我。
用一篇文章理解 Python 中的循环
读完这篇文章后,Loops 再也不会把你扔进 Loop 了
计算机擅长一遍又一遍地执行重复性任务,因为它们永远不会“感到无聊”或出错。一个简单的计算请求计算机可以执行一次或一千次,第一个结果将与最后一个结果一样准确。这是人类无法保证的。
能够将日常任务的自动化委托给计算机是一项无价的技能,每个程序员都应该掌握这项技能,以提高他们的编码技能和代码准确性。
在循环的帮助下,可执行的任务数量从将文件复制到网络上的一组计算机、向某些用户发送个性化电子邮件或 验证进程是否仍在运行。
不管任务有多复杂,你说多少次,计算机就会执行多少次,更重要的是,这让你有更多的时间去从事更有趣的活动。
在 Python 中,循环可以通过三种方式实现:
- While循环
- 为循环
- 递归
在本文中,我将解释这些技术,考虑到每一种技术采用的方法略有不同。你将学习如何编写代码,以及如何理解何时使用一种技术而不是其他技术。
目录:
1.While 循环(4 分钟读取)
2.对于循环(4 分钟读取)
3.递归(3 分钟读取)
1.While 循环
该技术指示计算机根据条件的值连续执行代码。它以关键字 while、开头,后跟要计算的比较,然后是一个冒号。下一行是要执行的代码块,向右缩进。类似于一个 if 语句,只有当比较结果为真时,才会执行主体中的代码。
然而,当循环分开时,设置的原因是只要评估语句为真,代码块就会继续执行。一旦语句不再为真,循环将退出,下一行代码将被执行。
看看下面的例子:
让我们仔细检查这个循环中的每一行代码:
- 在第一行中,我们将值 0 赋给以单词“iteration”命名的变量“I”。为了给变量一个初始值,这个动作叫做“初始化”。
- 在那之后的行中,我们开始了而循环。我们为这个循环设置了一个条件,即“ i ”必须小于 5。现在,“ i ”是 0,因为它刚刚被初始化,所以这个条件当前为真。
- 在接下来的两行中,我们有一个向右缩进的块。在这里,我们可以使用 Python 函数共有的特性,这表明共享相同数量缩进空间的每一行代码都将是函数体的一部分,或者在本例中是循环。
- 循环体中有两行。在第一行中,我们打印一条消息,后跟当前迭代,由值“ i ”表示。在第二行中,“ i 的值递增。我们通过在它的当前值上加 1 并将其赋回“ i ”来实现这一点。所以在第一次执行循环体之后,“ i ”将会是 1 而不是 0。
因为这是一个循环,所以计算机不会继续执行脚本中的下一行。相反,它会循环回去,重新评估 while 循环的条件。
因为这里的 1 仍然小于 5,所以它再次执行循环体。计算机将继续这样做,直到条件不再为真。在本例中,当“I”不再小于 5 时,条件将为假。一旦条件为假,循环结束,执行下一行代码。
避免无限循环陷阱
照片由 Grooveland 设计在 Unsplash 上拍摄
如前所述, while 循环使用条件来检查是否从循环结构中退出。 while 循环的主体需要确保被检查的条件将会改变。如果不改变,循环可能永远不会结束,我们会得到所谓的无限循环,一个不断执行且永不停止的循环。
以下面的代码为例。和前面的例子一样,我们初始化了“ i 变量,但是忘记在循环中添加一个索引来在每次迭代中刷新变量。因此,循环将一直运行,直到我们用 CTRL+C 命令手动中断它:
为了避免这个问题,花点时间考虑一个变量可以取的不同值是个好主意。这有助于确保循环在迭代过程中不会被卡住。
你应该总是试图避免无限循环吗?
虽然您需要小心无限循环,但它们并不总是坏事。
有时你实际上希望你的程序持续执行,直到某些外部条件被满足。
如果您在 Linux 或 macOS 系统上使用过 ping 实用程序,或者在 Windows 系统上使用过 ping-t ,您会看到一个无限循环。这个工具会一直发送数据包,并将结果打印到终端,除非你给它发送中断信号,通常是按 Ctrl+C 。
在 Python 中,我们通常使用关键字 break ,创建带有自动指示的循环来中断迭代,您可以在下面的代码中看到,这表示当前循环应该停止运行:
正如您所看到的,所指示的过程是为每次迭代将变量“I”加 1,直到它达到值 10,这时它应该中断该过程。代码的逻辑应该是这样的:
2.对于循环
一个 for 循环遍历一系列值。 for 循环的一个非常简单的例子是迭代一系列数字,例如从 0 到 4。
注意这个结构有点类似于循环时的结构。第一行表示关键字的区别,以冒号结尾。循环体向右缩进,就像在 while 循环、 if 块和函数定义中一样。这种情况下不同的是我们在中有了关键词。
此外,在关键字的和关键字的之间,我们有一个变量的名称,在本例中“I”代表“index”。该变量将接受循环遍历的序列中的每个值。所以在这个例子中,它将遍历使用 range() 函数生成的数字序列。
提醒一下,在 Python 和许多其他编程语言中,默认情况下,一系列数字将以值 0 开始。此外,生成的数字列表将比给定值小 1。在这个简单的例子中,“I”取值为 0、1、2、3 和 4。
在本文的这一点上,你可能会疑惑:为什么有两个看起来做同样事情的循环?
for 循环的强大之处在于,我们可以用它来迭代任意类型的值序列,而不仅仅是一系列数字。例如,我们可以迭代字符串或单词列表:
循环的迭代的序列可以包含任何类型的元素,而不仅仅是字符串。例如,我们可以迭代一系列数字来计算总和以及平均值。
在这个例子中,我们定义了一个值列表。之后,我们初始化两个变量,一些和长度,它们将在循环的主体中更新。在循环的中,我们遍历列表中的每个值,将当前值加到值的和中,然后将长度加 1,计算列表中有多少个元素。一旦我们完成了整个列表,我们就打印出总数和平均值。
每当我们想要迭代任何序列的元素并对它们进行操作时,我们将在示例中继续使用 for 循环。
如何确定使用哪个循环?
如果你想知道什么时候应该使用进行循环,什么时候应该使用而 l 哎呀,有一种方法可以告诉你:
- 当有一个元素序列需要迭代时,使用进行循环。
- 当想要重复一个动作直到条件改变时,使用 while 循环。
如果在循环时,无论你想做什么都可以用代替或来完成,就用你最喜欢的那个。
提升你的循环技能:嵌套循环
基本上,嵌套循环是另一个循环中一个或多个 for 循环。例如,假设你要准备一场网球锦标赛的时间表,我们最喜欢的四名选手将互相比赛。
为了准备我们的时间表,让我们用 Python 脚本对球员进行排序,该脚本将输出所有可能的比赛配对。为此,名字的顺序很重要,因为对于每场比赛,第一个名字将是一个玩家,第二个名字将是竞争者。当然,我们不想做的是有一个玩家和自己作对。为此,我们需要使用一个条件来确保我们只在名称不同时打印配对。
如您所见,嵌套循环对于解决某些问题非常有用,例如在锦标赛中对球员进行排序。
循环的常见错误
- 遍历非序列:正如我已经提到的, for 循环遍历序列。因此,Python 的解释器将拒绝迭代单个元素,比如整数或者不可迭代的对象。
- 初始化变量失败。确保循环条件中使用的所有变量在循环前都已初始化。
- 意外的无限循环。确保循环体修改了条件中使用的变量,这样循环将最终针对变量的所有可能值结束。
- 忘记了不包括范围的上限()。
作为第一个场景的实际例子,让我们尝试迭代一个整数:
这个问题有两种解决方案,这取决于我们要做什么:
- 如果你想从 0 到 25,那么我们使用范围功能。
- 如果你试图迭代一个只有 25 个元素的列表,那么它必须是一个列表。
3.递归
递归是 Python 中除了循环遍历一系列值的第三种机制,而和用于循环。
虽然递归是软件工程中非常常见的技术,但它在自动化任务中并不常用。尽管如此,了解它还是很有价值的,因为您可能会在别人的代码中遇到它,或者更重要的是,您可能会面临递归是解决它的最佳方式的问题。
递归是对较小的问题重复应用相同的过程。
这些方法的一个很好的视觉例子是俄罗斯套娃:
正如你可能看到的,每个娃娃里面都有一个更小的娃娃。当你打开娃娃,发现里面有一个更小的娃娃时,你继续往前走,直到你碰到那个打不开的最小的娃娃。递归让我们通过将问题简化来解决复杂的问题。
假设我们想知道总共有多少个娃娃,我们需要循环遍历每个娃娃,直到找到最后一个,然后计算我们打开了多少个娃娃。这就是递归在起作用。
在编程中,递归是一种通过函数调用自身来完成重复任务的方法。递归函数通常用修改后的参数调用自己,直到达到特定的条件。这种情况称为基本情况。
在俄罗斯娃娃的例子中,最小的娃娃将是基础案例。让我们尝试一个现实生活中的问题,其中我们将尝试编写一个计算数字阶乘的函数。在数学中,正整数 n 的阶乘,用 n 表示!,是所有小于等于 n 的正整数的乘积:
正如您在上面的代码中所看到的,函数 factorial() 调用自身来求解大于 1 的数字的阶乘。每次执行该函数时,它都用一个较小的数字调用自己,直到它到达基例。一旦它到达基本情况,它就返回值 1。这个循环将继续下去,直到第一个 factorial() 函数返回期望的结果。
您可能想知道,如果我可以只使用 for 或 while 循环,为什么我们还需要递归函数?
因为在使用递归函数时,一些特定问题的解决方案更容易编写和理解。例如,对于试图自动化任务的 IT 专家来说,递归将是检查计算机目录的有用工具,因为每个目录都包含包含文件的子目录。
基本情况是一个没有子目录的目录。对于这种情况,该函数将只返回文件的数量,但是对于剩余的子目录,它将调用递归函数。当操作递归结构时,使用递归函数通常比 for 或 while 循环更容易。
重要的是要指出,在某些语言中,你可以使用最大数量的递归调用。在 Python 中,默认情况下,可以调用递归函数 1000 次,直到达到极限。这对于子目录或类似的递归结构来说很好。
结论
在这篇文章中,我解释了如何告诉计算机重复做一个动作。Python 给了我们三种不同的方法来执行重复的任务: while 循环,用于循环,递归。
For 当你想要迭代一个已知的元素序列,但是当你想要在某个条件为真的情况下操作时,循环是最好的选择。
如果你喜欢这篇文章中的信息,不要犹豫,联系我分享你的想法。它激励我继续分享!
深入了解 Python 编程的相关文章:
创建股票价格模拟器:
几何布朗运动过程的简单应用来模拟股票价格。
towardsdatascience.com](/create-a-stock-price-simulator-with-python-b08a184f197d)
了解如何用 Python 编写令人惊叹的可视化代码:
我准备了一份详尽的指南来展示漂亮的可视化效果,以增强度量、KPI、预测和其他…
towardsdatascience.com](/business-intelligence-visualizations-with-python-1d2d30ce8bd9)
用 Python 分析纽约的数据:
发现最方便的租赁方式,以便继续实施具有良好可视化效果的数据分析。
towardsdatascience.com](/airbnb-rental-analysis-of-new-york-using-python-a6e1b2ecd7dc)
感谢您花时间阅读我的文章!如果您有任何问题或想法要分享,请随时联系我的电子邮件,或者您可以在以下社交网络中找到我以了解更多相关内容:
用一篇文章理解机器学习
让我们深入了解市场上最令人兴奋、要求最高的学科之一。
摄影爱好在 Unsplash 上
我们所知道的事情中,很少有不能简化为数学推理的。当他们不能时,这表明我们对他们的了解非常少而且混乱。
《机会法则》,序言(1962)——约翰·阿巴斯诺特
与其他任何数学学科相比,统计学更大程度上是时间的产物。如果前几个世纪的科学家能够接触到真正的计算能力,甚至是计算机,他们的研究和当前的领域作为一个整体将会描绘出一幅完全不同的画面。
虽然不常讲,但统计学是今天已知的机器学习的基础。如今,很难找到一个用例,其中机器学习没有以某种形式应用,统计数据被放在一边。随着越来越多的人意识到 ML 应用程序的好处,并获得其部署的简单性,这种趋势可能会继续增长。如果你喜欢学习更多这方面的知识,或者你只是好奇它是如何工作的,这篇文章可能适合你。
目录:
- 什么是机器学习?我为什么要用它?(2 分钟读取)
- 流行的误解(1 分钟阅读)
- 机器学习和计量经济学有什么不同?(1 分钟读取)
- 必读的机器学习书籍(1 分钟阅读)
- 金融中的机器学习(1 分钟阅读)
- 用 Python 编程支持向量机和线性回归模型(4 分钟阅读)
1.什么是机器学习?
广义而言,机器学习是指在高维空间中学习复杂模式的一组算法。我们来澄清一下这个说法:
- ML 模型在没有接受任何特定指导的情况下学习模式,因为研究人员对数据施加很少的结构。相反,算法从数据中导出结构。
- 我们说它学习复杂的模式,因为由算法识别的结构可能无法表示为有限的方程组。
- 高维空间这个术语指的是在处理大量变量的同时寻找解,以及变量之间的相互作用。
例如,我们可以通过展示示例并让模型推断人脸的模式和结构,来训练 ML 算法识别人脸。我们没有定义面部,因此算法在没有我们指导的情况下学习。
ML 涉及三种类型的学习:
- 监督学习:学习方法,包括手动告诉模型用户想要为训练数据集预测什么标签。起点是程序员希望算法尊重的一组特征和结果标签。例如:回归,分类。
- 无监督学习:学习方法,包括让模型根据每个元素具有的更明显的特征来确定数据集的标签和分组元素。例如:聚类、表示。
- 强化学习:智能体通过与环境互动,从环境中学习并因执行动作而获得奖励的学习方法。类似于监督学习,它接收一种指南,但它不是来自程序员的输入。
为什么要用?
机器学习正在改变我们生活的方方面面。如今,算法可以完成直到最近只有专家才能完成的任务,即使没有这样的专业水平也逐渐可以实现。另一方面,传统的数据分析技术保持静态,尽管在定义了数据结构的情况下非常有用,例如:
发现最方便的租赁方式,以便继续实施具有良好可视化效果的数据分析。
towardsdatascience.com](/airbnb-rental-analysis-of-new-york-using-python-a6e1b2ecd7dc)
在快速变化的非结构化数据输入的情况下,传统数据分析的用途有限,而非结构化数据输入最适合监督和非监督学习等技术的应用。这时,能够分析数十种输入和变量的自动化流程成为宝贵的工具。
除此之外,ML 技术实现的解决过程****与传统的统计和数据分析有很大不同,因为它们专注于从用户那里接收确定目标的输入,并了解哪些因素对实现该目标很重要,而不是由用户来设置将决定目标变量结果的因素。
它不仅允许算法进行预测,还允许算法与预测进行比较,并调整结果的准确性。
2.流行的误解
ML 是圣杯还是没用?
围绕着 ML 的炒作和反炒作多得令人难以置信。第一个创造了在可预见的未来可能无法实现的期望。相反,反宣传试图让观众相信 ML 没有什么特别之处,经典统计学产生的结果与 ML 从业者和爱好者声称的一样。这两个极端都阻止了用户和爱好者认识到它今天所提供的真正的和不同的价值,因为它有助于克服经典技术的许多限制。
ML 是一个黑盒
这是流传最广的神话。显然,这并不像马科斯·洛佩斯·德·普拉多 在其著作《资产管理者的机器学习》中的论点那样,他指出,在某种程度上,ML 技术与科学方法是兼容的,因为它们已经被应用于世界上的每个实验室,用于药物开发、基因组研究和高能物理等实践。无论是否有人在黑盒中应用它,这都是个人的选择,当然,如果理论在最初被解释的模型后面,更好的使用应用将被设计出来。
3.ML 和计量经济学有什么区别?
计量经济学的目标是推断统计模型的参数,以便能够解释变量并根据这些参数得出结论,因为它们本身就有意义。例如,线性回归系数表示每个自变量和因变量之间是正相关还是负相关。计量经济学既不是专注于用模型预测变量的值,也不是试图让模型在这个意义上竞争来增强预测。
硬币的另一面是 ML 分析师,他们的分析纯粹是为了优化预测过程而设计的,与参数解释或模型解释无关,如上所述,这有时是不可能做到的。虽然它们的目标不同,但这两个学科是互补的,并且具有可相互应用的宝贵工具。
4.必读的机器学习书籍
机器学习潜在的读书人有两种:从业者和学院派。对于他们中的每一个人,都有一系列的文献要处理,考虑到第一组人本质上是在寻找更多的应用技术,而不是专注于模型背后的数学。另一方面,学者们可能会在他们将利用的算法背后寻找严格而有力的实证。
以下是对每组的简要建议:
学术界:
- 詹姆斯,g .等人(2013)——统计学习介绍——施普林格
- Bishop,c .(2011)——模式识别和机器学习——Springer
- 马斯兰德,s .(2014)——机器学习:算法视角——第二版。—查普曼&大厅
- 萨顿,r .和巴尔托,a .(2018)——强化学习:介绍——第二版——麻省理工学院出版社
- Hastie,t .等人(2009 年)——统计学习的要素——第二版——施普林格
- Aggarwal,C. (2018) — 神经网络和深度学习 — Springer
从业者:
- Geron,a .(2017)——用 Scikit-learn&tensor flow——O ' Reilly 进行动手机器学习
- Chollet,F. (2018) — 用 Python 进行深度学习 — Manning
- 洛佩兹·德·普拉多(2018)——金融机器学习的进展——威利
5.金融中的机器学习
金融应用对统计学和 ML 提出了完全不同的挑战,因为经济系统表现出一定程度的复杂性,超出了经典统计工具的掌握范围。因此,随着大型数据集、更强大的计算能力和更高效的算法交付给学生、分析师和整个社区,ML 将越来越多地在该领域发挥重要作用,这一趋势不太可能改变。
金融应用程序的主要区别在于:
- 平稳性:在金融领域,我们使用非平稳数据,例如资产价格和其他时间序列数据集,而在其他科学领域,数据往往是平稳的,这意味着信息取决于时间段。此外,财务数据往往至少部分相关,这增加了分析的复杂性。
- 信噪比:基本上,这方面指的是根据当前输入,我们可以预测多少未来数据。在金融领域,今天的数据对未来结果的预测能力往往较低,而在药物开发或医疗应用等普通科学领域,情况并非如此。在这些领域中,实验程序在一致和不变的条件下进行,以消除波动性给等式带来的不确定性,例如市场每天必须处理的无数信息源。
这个问题并不意味着 ML 不能应用于金融,而是它必须以不同的方式应用。 - 结果的可解释性:由于金融法规和机构的合规义务,与其他科学领域面临的审查水平相比,在金融战略中论证 ML 模型的应用对项目经理和资产经理来说是一个挑战。
在最后一部分中,我将执行两个样本模型的 Python 应用程序,支持向量机和线性回归,以便比较它们,并在实践中展示 ML 脚本是如何实现的。如果你不想进入编码领域,也许你会发现这篇文章很有用:
我准备了一个简单的应用程序,向您展示如何借助一个有趣的工具来实现这一点,这个工具叫做…
towardsdatascience.com](/is-it-possible-to-make-machine-learning-algorithms-without-coding-cb1aadf72f5a)
6.用 Python 实现支持向量机和线性回归;
让我们深入编码吧!
我们将开始导入必要的库: Scikit-Learn 和 Numpy 。
Scikit-Learn 是一个开源的机器学习库,支持监督和非监督学习。它还提供了各种工具,用于模型拟合、数据预处理以及模型选择和评估等。在这种情况下,我们将利用:
- Model_selection 类 :包含用于在训练和测试样本中拆分数据集的工具、超参数优化器和模型验证器。
- Linear_model 类 :用于实现各种线性模型,如逻辑回归、线性回归等。
- svm 类 :用于实现用于分类、回归和离群点检测的监督学习方法。
另一方面, Numpy 是一个库,它提供了对大型多维数组和矩阵的支持,以及对这些数组进行操作的大量高级数学函数。这是机器学习堆栈基础的一部分。
# Imports
import numpy as np
import sklearn.model_selection as sk_ms
import sklearn.linear_model as sk_lm
import sklearn.svm as sk_sv
在那之后,我们继续创建我们将要使用的 Numpy 数组。考虑这些将作为“合成”数据集,因为这个过程也可以用. csv 文件或数据库来执行。
首先,我将生成 1000 行和 5 个要素的随机样本,作为模型的输入。除此之外,我还特意包含了一个带有预定数组的示例函数。这个函数执行的过程包括计算输入数组中每个元素与 X 数组中每个元素的乘积。
# Setting seed to lock random selection
np.random.seed(1234)# Generate random uniform-distributed sample
X = np.random.uniform(size=(1000,5))
y = 2\. + X @ np.array([1., 3., -2., 7., 5.]) + np.random.normal(size=1000)
为了澄清前面的代码,请参考下面的笔记本,查看所创建变量的输出:
这些是每个阵列的形状(长度和尺寸):
在我们创建了样本数据集之后,我们必须继续进行我在这篇文章中解释的训练和测试子集的划分。这个任务可以用已经引入的 scikit-learn 包的类来执行,称为 model_selection , train_test_split 方法:
在接下来的步骤中,我将实现两个模型,即线性回归和支持向量回归。作为每个模型的概述:
- 线性回归:这是一种统计模型,假设响应变量(y)是权重的线性组合,也称为参数,乘以一组预测变量(x)。该模型包括考虑随机采样噪声的误差。对于术语响应变量,我的意思是由输出值显示的行为取决于另一组因素,称为预测因素。
在下面的等式中,您将看到包含的β是权重,x 是预测变量的值,ε是误差项,表示随机采样噪声或模型中未包含的变量的影响。
传统线性回归方程
- 支持向量机:这也是一个用于分类和回归问题的线性模型,它基本上由一个算法组成,该算法将数据作为输入,并输出一条线,该线将观察结果分成两类。
在下面的要点中,您将找到编写两个引入模型的脚本,在其中我们可以看到每个模型的结果平均值。为了比较这些模型,我运行了一个 交叉验证 过程,这是一种将我们的数据分割成一定数量的子集的方法,称为折叠(在本例中为五个),以避免过度拟合我们的模型:
最后,我们开始评估测试样本上的模型,并获得系数和截距的输出:
结论
我写这篇文章的目的是以一种简单的方式传递一些机器学习的基础知识,并稍微关注一下金融,因为这是我个人感兴趣的一个主题。我相信,投资者将逐步被引入算法交易和涉及人工智能和机器学习过程的智能资产管理实践,因此在某种程度上,本文试图诱导更多的人参与这场运动,以便成为变革的一部分。
我希望我已经实现了我的目标,并且对你有用。如果你喜欢这篇文章中的信息,不要犹豫,联系我分享你的想法。它激励我继续分享!
参考
- [1]Numpy 的基本指南 —西达尔特·迪克西特— 2018
- 2 支持向量机 — Rushikesh Pupale — 2018
感谢您花时间阅读我的文章!如果您有任何问题或想法要分享,请随时通过我的电子邮件联系我,或者您可以在以下社交网络中找到我以了解更多相关内容:
理解 map()函数操纵熊猫系列
熊猫基础知识
了解使用 map()函数将数据转换为所需格式的基本原理
当我们处理真实世界的数据时,数据集很少或者很可能从来没有为我们的分析目的准备好确切的格式。因此,数据处理阶段的一个重要步骤是将数据从原始的不良格式转换成便于分析的格式。
除了处理缺失数据和异常值,另一个重要的数据转换过程是数据映射。最初作为一个数学概念,映射是从一组现有值创建一组新值的过程,通常是一对一的。作为数据科学研究中最受欢迎的 Python 库之一,pandas 库为我们提供了操作系列数据的map()
函数。一旦您很好地理解了map()
函数,我们就可以在以后的文章中继续研究另一个更强大的数据操作函数apply()
。
熊猫系列
在 pandas 中,系列是一个类似一维数组的对象,包含一系列值。这些值中的每一个都与一个标签相关联,该标签被称为指数。我们可以通过传入一个类似数组的对象(例如 list)或一个字典来创建一个序列。下面是一些常见的例子。
熊猫系列
在上图中,我们首先使用列表创建了一个系列。如果我们不设置可选的index
参数,序列将使用从 0 开始的默认整数索引。name
参数也是可选的。当它被指定时,它成为系列的名称。
我们还使用字典创建了一个系列,字典的值构成了系列的值。这些键自动成为系列的索引。
map()的基本语法
map()
函数的语法如下:Series.map(self, arg, na_action=None)
。如你所见,这个函数的调用者是一个熊猫系列,我们可以说map()
函数是一个系列对象的实例方法。想了解更多关于函数中self
参数的内容,可以参考我之前的文章。
在 Python 中使用 self
medium.com](https://medium.com/better-programming/unlock-the-4-mysteries-of-self-in-python-d1913fbb8e16)
na_action
论证
na_action
参数用于指定我们如何处理NaN
值。该参数的默认选项是None
,使用它将原始数据中的 NA 值传递给映射函数(即arg
参数)。该参数的另一个选项是‘ignore’
,使用它不会对原始数据中的 NA 值执行任何操作。
以下示例显示了这两个选项在处理序列中最后一个数据点(即安娜值)方面的差异。当na_action
为‘ignore’
时,映射函数不会尝试对 NA 值做任何事情,并将映射值设置为NaN
。相反,当na_action
为None
时,映射函数将使用 NA 值(即nan
)来创建映射值(即Number: nan
)。
“无”和“忽略”的区别
数据转换(arg 参数)
map()函数有趣的部分主要是关于我们如何使用arg
参数。具体来说,arg
参数向函数给出了如何将现有数据映射到新数据的指令。该参数可以设置为函数或字典。让我们来看看它们是如何工作的。
使用 Lambda 函数
实际上,在上面关于na_action
选项的例子中,您看到我们使用了 lambda 函数来映射数据。如果你不知道什么是 Python lambda 函数,请参考我之前关于这个主题的文章。
使用 Python lambdas 的最佳实践
medium.com](https://medium.com/better-programming/the-top-4-misuses-of-lambdas-in-python-e419f426b74f)
简言之,lambda 函数被称为匿名函数,它们的基本语法是lambda arguments: expression
。具体来说,声明由关键字lambda
表示,后面是参数列表(0 个或更多),以及指定对这些参数执行什么操作的表达式。
为了给你一个关于 lambda 函数的更实际的用例,让我们考虑下面的例子。在现实生活中的数据,我们可能有百分比数据表示为字符串。我们可以编写一个 lambda 函数,将字符串数据转换为数字数据,这是一种更便于分析的格式。
map()中的 Lambda 函数
在上面的例子中,我们获取原始数据的值,并使用切片技术获得一个子串。相关知识点是字符串中最后一个字符的索引为-1。所以表达式x[:-1]
将创建一个从原始字符串的开始到最后一个字符的字符串(不包含)。
使用常规函数
除了使用 lambda 函数,我们还可以使用内置或自定义的函数。这些函数需要能够映射系列中的每个数据点,否则将会出现错误。
map()中的内置函数和自定义函数
在上图中,我们首先创建了一个由姓名列表组成的序列。第一项任务是为这些人创建姓名首字母。为此,我们编写了一个名为initials()
的函数。这个函数接受序列的值。我们使用列表理解技术来创建每个名字的首字母列表,并连接这些字母来创建最终输出作为映射值。如您所见,映射后,新系列包含所有人的姓名首字母。
除了使用自定义函数,我们还可以使用一些内置函数来生成新的系列。例如,在上面的代码中,我们可以简单地使用len()
函数,它将计算新系列中名称的长度(包括空格)。
使用字典
在大多数情况下,我们应该能够为arg
参数设置一个如上的函数来满足我们的需求。然而,我们也可以使用字典作为映射函数,尽管我们不经常使用它。这是这种用法的一个简单例子。
地图中的字典()
如上所示,我们创建了一个从 0 到 4 的整数序列。然后我们创建了一个字典,它有从 1 到 4 的键,这个字典被用作map()
函数调用的arg
参数。
需要注意的一点是,如果在字典中没有找到这些值,那么映射的值将是NaN
。但是,我们可以使用defaultdict
数据类型来代替dict
数据类型(关于defaultdict
的更多信息,请参考我的上一篇文章)。在这种情况下,NaN
值将被从defaultdict
对象生成的默认值替换。有关此功能,请参见以下示例。
map()中的 Defaultdict
在你走之前
数据操作是预处理数据的重要步骤。Pandas 系列可以看作是更加灵活和强大的 DataFrame 对象的构建块。因此,了解map()
函数的使用可以方便你对 DataFrame 数据的操作,关于这一点,我们可以在后面进行更多的讨论。
感谢阅读。用于本教程的笔记本可以在 GitHub 上找到。
关于作者
我写关于 Python 和数据处理与分析的博客。以防你错过了我之前的一些博客。以下是与本文相关的几篇文章的链接。
更好的 Python
medium.com](https://medium.com/better-programming/30-simple-tricks-to-level-up-your-python-coding-5b625c15b79a) [## 掌握 Python 中列表理解的 9 件事
本教程将帮助你学习 Python 中列表理解的最常见用法
medium.com](https://medium.com/better-programming/9-things-to-know-to-master-list-comprehensions-in-python-8bc0411ec2ed) [## 为什么在做其他事情之前应该首先可视化数据的 3 个简单原因
我们大脑处理的 90%的信息是视觉信息,我们大脑处理视觉信息的速度比…
towardsdatascience.com](/3-simple-reasons-why-you-should-first-visualize-data-before-doing-anything-else-63ec05d86d9)
理解神经网络和模型泛化
深度神经网络的模型泛化、过拟合和正则化方法的挑战
在几个与神经网络相关的人工智能项目之后,我意识到模型的归纳能力是人工智能项目成功的核心。我想写这篇文章来帮助读者理解如何通过正则化方法来优化模型的性能,并更好地理解交付基于神经网络的可靠和可扩展的人工智能解决方案的复杂性。
泛化是一个用来描述模型对新数据做出反应的能力的术语。
泛化是你的模型经过训练消化新数据并做出准确预测后的能力。这可能是你的人工智能项目中最重要的元素。模型的概括能力是人工智能项目成功的关键。事实上,我们担心的是,一个模型在训练数据上训练得太好了,但却无法推广。
因为这个原因,我们经常达不到生产阶段…当给定新数据时,它会做出不准确的预测,使模型无用,即使它能够对训练数据做出准确的预测。这叫过度拟合。
相反的情况也可能发生。欠拟合是指模型在数据上没有得到足够的训练。在拟合不足的情况下,它使模型变得毫无用处,并且它不能做出准确的预测,即使使用训练数据也是如此。
在所有人工智能项目中,我们在现有数据上建立我们的模型,并希望它们能够完美地适应(推广)新数据。在监督学习中,我们拥有过去的数据,包括所有预测值和我们希望预测的真实值。尽管定义业务问题、收集相关数据、清理和准备数据以及构建模型都具有挑战性并且非常耗时……但另一个挑战仍然存在— 如何知道模型是否会很好地预测未来?
训练一个可以很好地推广到新数据的深度神经网络是一个具有挑战性的问题。
就神经网络而言,正则化是一种对学习算法进行轻微修改的技术,以便模型更好地推广。反过来,这也提高了模型在不可见数据上的性能。
模型复杂性
从商业角度来看,深度神经网络的主要优势是,随着数据集越来越大,它们的性能会不断提高。当公司试图创造数据网络效果时,这是非常有趣的。
然而,一个几乎有无穷多个例子的模型最终会达到网络学习能力的极限。适当的正则化是更好的泛化性能的关键原因,因为深度神经网络通常过参数化,并且可能遭受过拟合问题。
我们可以通过以下方式降低神经网络的复杂性,从而减少过拟合:
减小模型的容量可以降低模型过度拟合定型数据集的可能性,使其不再过度拟合。
通过保持较小的网络权重来减少过拟合的技术被称为正则化方法。
正则化:一类添加额外信息以将不适定问题转化为更稳定的适定问题的方法。
下面,我列出了几种我们经常使用的正则化方法(其他方法确实存在比如权重约束或者活动正则化)。然而,减少过度拟合的最简单的方法是从本质上限制你的模型的容量。
根据我的经验,这种方法是迄今为止最有效的正则化技术。在网络中的两个连续层之间应用 Dropout。在每次迭代中,关键思想是从神经网络中随机删除单元(以及它们的连接)。这导致后续层依赖于其与前一层的所有连接。这种方法通过防止训练数据上复杂的共同适应来帮助对抗过度适应。
全连接(FC)层最容易过度拟合,因为它们包含的参数最多。应该对这些层应用断开(影响它们与下一层的连接)。除了标准形式的辍学,还有几种辍学的变化,旨在进一步提高泛化性能。比如自适应退学,退学率由另一个神经网络动态决定…
然而,我注意到,如果你使用 CNN,dropout 现在不太常用了。反而看到越来越多的数据科学家使用批量归一化。当你拥有庞大的数据集时,批量规范化比丢弃更有效。
噪声 一种常见的正则化类型是在训练期间注入噪声:向神经网络的隐藏单元添加或乘以噪声。通过在训练深度神经网络时允许一些不准确性,不仅可以提高训练性能,而且可以提高模型的准确性。
根据 Jason Brownlee 的说法,训练中最常见的噪声类型是将高斯噪声添加到输入变量中。添加的噪声量(如扩散或标准偏差)是一个可配置的超参数。太少的噪声没有效果,而太多的噪声使得映射函数太难学习。请确保在评估模型期间,或者在使用模型对新数据进行预测时,没有添加任何噪声源。
提前停止
提前停止是一种交叉验证策略,我们保留训练集的一部分作为验证集。实际上,当我们看到验证集的性能越来越差时,我们会停止对模型的训练。
换句话说,这种方法试图在开始对噪声建模之前,在估计器已经学会从数据中提取所有有意义的关系时,尽早停止估计器的训练阶段。
这是通过监控验证损失(或另一个验证度量)并在该特定度量停止改善时结束训练阶段来实现的。通过这样做,我们给了估计器足够的时间来学习有用的信息,但是没有足够的时间从噪声中学习。
我对这种方法的问题是,不能保证在任何给定的时间点,模型不会再次开始改善。比提前停止更实际的方法是存储在验证集上实现最佳性能的模型的权重…
迁移学习
这种方法是通过将您的网络的权重初始化为另一个具有相同架构的网络的权重来完成的在大型通用数据集上进行预训练。我们经常在计算机视觉项目中使用这种方法。当我们的业务问题没有太多数据时,它对我们帮助很大,但我们可以找到另一个有数据的类似问题。在这种情况下,我们可以使用迁移学习来减少过拟合。
批量标准化 批量标准化(BN)是一种标准化网络输入的技术,适用于前一层的激活或直接输入。这种方法允许网络的每一层都能够独立于其他层进行学习。BN 用于通过调整和缩放激活来标准化输入层。
批量标准化(BN)是一种标准化深度神经网络中间层激活的技术。
除了具有正则化效果之外,批处理规范化还在其他方面帮助您的模型(允许使用更高的学习率等)。).我建议您验证各图层的权重和偏差分布看起来近似标准正态。
由于 BN 有一种正规化的效果,这也意味着你可以经常取消辍学(这是有帮助的,因为辍学通常会减慢训练)。
在训练期间,我们随着神经网络的权重和偏差更新批量标准化参数。对批处理规范化的一个更重要的观察是,批处理规范化作为一种规范化,因为使用小批处理显示出随机性。
批量 =一次正向/反向传递中训练样本的数量。批量越大,需要的内存空间就越大。
数据增强。处理过度拟合的另一种方法是提高数据质量。你可能会想到异常值/噪声去除,然而,实际上,它们的效率相当有限。另一种有趣的方式(特别是在图像相关的任务中)是数据增强。目标是随机转换训练示例,以便在模型看来它们不同时,它们传达相同的语义信息。就我个人而言,当我看到我的模型在训练集上接近 0 损失时,我开始考虑使用数据增强。
建议
我建议在考虑正则化方法之前先经历一些基本步骤。事实上,大多数时候,我们不能确定对于每个学习问题,都存在一个可学习的神经网络模型,它可以产生尽可能低的泛化误差。
正确的期望
首先要找到一个好的参考,它会告诉你,在你的数据集上,或者在你能找到参考的最相似的数据集上,存在一个可以达到你所寻找的泛化误差的架构。在您在自己的数据集上进行训练之前,尝试在这些参考数据集上再现这样的结果是一件有趣的事情,这是对您所有基础设施都已正确就位的测试。
培训程序验证
检查你的训练程序是否正确也很重要。这些检查包括以下内容:
超参数/架构搜索
最后,理解正则化本身并不一定意味着您的概化误差会变小,这一点很关键:模型必须具有足够大的容量才能实现良好的概化属性。这通常意味着,在你看到正规化的好处之前,你需要一个足够深的关系网。
如果没有其他帮助,您将不得不测试多个不同的超参数设置(贝叶斯优化可能会有所帮助)或多个不同的架构变化。
关于这个话题的更多信息,我推荐这些链接:
-https://machineellingmastery . com/train-neural-networks-with-noise-to-reduce-over fitting/
-https://towardsdatascience . com/batch-normalization-in-neural-networks-1ac 91516821 c
-https://papers . nips . cc/paper/5032-adaptive-dropout-for-training-deep-neural-networks
用一篇文章理解 Python 中的 O.O.P
深入面向对象编程以增长您的 Python 知识,重点关注真实世界的概念和类表示。
软件中的另一个技巧是通过使用已经编写好的部分来避免重写软件,这就是所谓的组件方法,这种方法的最新术语是所谓的面向对象编程。——比尔·盖茨。
比尔·盖茨的这句话说明了这篇文章的目的。理解面向对象编程允许你开发一种思考和实现代码的方式,我的目标是让它对你来说非常简单。毕竟,我们编码的主要原因之一,或者至少想学习如何编码,是为了自动化日常任务。正如微软的联合创始人所说,O.O.P .是实现这一目的的一种手段。
目录:
1.面向对象编程介绍(3 分钟阅读)
2.定义新类别(3 分钟阅读)
3.实例方法(1 分钟读取)
4.定义构造函数和其他特殊方法(2 分钟阅读)
5.代码重用(2 分钟读取)
1.面向对象编程简介
为了开始理解这种编程技术背后的直觉,让我们看一个初始的例子。想象一下,你必须向一个从未见过汽车的人描述一辆汽车,你会怎么做?
你可能想开始说它是一种用于运输的轮式机动车辆。你也可以说有几个品牌的汽车制造公司,它们生产不同类型的汽车来满足不同的需求。在一个更基本的层面上,你可能会说它有四个轮胎,在大多数情况下,它们可以承载五个人,并且它们主要用于运输人员,而不是货物。
当向计算机解释这是什么类型的物体时,用类似的方式接近它是一个好主意。计算机并不知道汽车是什么,为什么要制造汽车,或者谁在使用汽车。如果你想让你的计算机正确理解这个物体,在这个例子中是一辆汽车,你必须清楚地解释哪些是它的属性。
为了让计算机更容易理解这些新概念,Python 使用了一种叫做面向对象编程的编程模式,使用类和对象对概念进行建模。这是一个灵活、强大的范例,其中类表示和定义概念,而对象是 类的实例。在汽车示例中,我们可以创建一个名为 car 的类,向计算机定义其特征。我们将能够创建那个类 car 的数千个实例,它们是那个类的单个对象。
面向对象编程的想法可能听起来抽象而复杂,但是如果你已经编写了任何软件,你可能已经使用了对象,甚至没有意识到这一点。Python 中几乎所有的东西都是对象,所有的数字、字符串、列表和字典都包含在这种类型的元素中。面向对象编程的核心概念归结为与类型相关联的属性和方法:
- 属性是与类型相关的特征。
- 方法是与类型相关联的函数。
在汽车示例中,颜色和品牌是与程序创建的每个实例或汽车相关联的两个属性。另一方面,方法可以是由汽车执行的动作,例如驾驶。一个更面向计算机的例子是目录中的文件,因为每个文件都有名称、大小和创建日期。
如上图所示,当我们像刚才一样使用 type 函数时,Python 会告诉我们值或变量属于哪个类。由于整数和字符串是类,它们有一堆与之相关的属性和方法。您可以使用 Python 中的 dir 函数访问类的属性和方法,如下所示:
在我之前关于字符串的文章中,我探索了 string 类的许多方法和属性。看看它,进一步了解如何处理字符串对象,在这种情况下,它将是字符串类的另一个实例。这意味着它们都有相同的方法,尽管字符串中的内容不同。
为什么会有一堆以双下划线开头和结尾的方法?
这些被称为特殊方法,它们通常不会被冠以那些奇怪的名字。相反,它们由一些内部 Python 函数调用。例如, len 方法由 len 函数调用。
如果你想知道一个特定的方法做什么,你应该使用帮助功能:
当我们对任何变量或值使用 help 函数时,我们正在访问相应类的所有文档。在这种情况下,我们正在查看 str 和 int 类的文档。
尽管 Python 为我们提供了许多预定义的类,但是当我们用自己的属性和方法定义自己的类时,面向对象编程的威力就显现出来了。
2.定义新类别
如前所述,面向对象编程的要点是以计算机理解的方式帮助定义现实世界的概念。让我们动手以汽车为例构建一个新的类:
让我们来阐明准则的具体内容:
- 在 Python 中,我们使用 class reserved 关键字告诉计算机我们正在开始一个新的类。我们在后面加上类名和冒号。Python 风格指南建议类名应该以大写字母开头。在我这里,这个类叫做车。
- 在带有类定义的那一行之后是类的主体,按照循环或函数的模式向右缩进。
- 我们将在本文的第四部分讨论特殊方法 init 和 repr 。
我们如何扩展汽车类别的定义?它可能具有相同的属性,代表我们想要与汽车相关联的信息,如品牌和颜色。
现在,让我们继续创建 Car 类的一个实例,并将它赋给一个名为“my_car”的变量。为了创建任何类的新实例,我们调用类名,就像调用函数一样:
如您所见,我将 brand 和 color 作为参数传递,因为我已经配置了我的类,在创建该类的新对象时需要这两项。因此,我们可以调用创建的实例的属性,并接收先前分配的值:
用于访问属性的语法称为点符号,因为表达式中使用了点。点符号让你可以使用该物体可能拥有的任何能力,例如商标或颜色。
一些对象的属性和方法可以是其他对象,并且可以有自己的属性和方法。例如,我们可以使用 upper 或 capitalize 方法来修改我的实例的两个字符串属性:
到目前为止,我们已经创建了 Car 类的一个实例,并设置了它的属性。现在,我们可以创建具有不同属性的类的新实例:
3.实例方法
本质上,调用方法是为了让对象做一些事情。比如上和大写为弦。学习 O.O.P .中方法直觉的关键是理解方法是对类的特定实例的属性进行操作的函数。当我们在字符串上调用 lower 方法时,我们将特定字符串的内容变成小写。
让我们通过创建一个名为 Dog 的新类并定义我们自己的方法来更深入地了解一下。首先,我们需要定义这个类,并创建它的一个实例,就像我们之前对 Car 类所做的那样。虽然我的狗可能很棒,但只要我不为它们定义方法,它就不能执行任何动作。看一下这个例子:
如图所示,我们必须开始用 def 关键字定义一个方法,就像我们定义一个函数一样,并且将方法体向右缩进,就像我们定义一个函数一样。
该函数正在接收一个名为“self”的参数。此参数表示正在其上执行该方法的实例。
即使我的狗叫,它也总是用同样的方式。我们可以通过简单地修改代码来改变它的运行方式,以便在我们为类配置的属性和方法中获得灵活性:
4.定义构造函数和其他特殊方法
到目前为止创建的两个类都包含默认值作为属性和方法。这不是一个理想的场景,因为它为每个属性创建了冗余的代码,更重要的是,因为它很容易忘记设置一个重要的值。
因此,在编写代码时,最好在创建类时设置随实例变化的属性和方法,以确保每个实例包含相同的重要属性。为了做到这一点,我们需要使用一个叫做构造函数的特殊方法。
类的构造函数是当你调用类名时调用的方法。它总是被命名为 init 。
它包含关键字 self,它指的是正在创建的实例,以及在实例创建后将由程序员作为属性传递的参数,就像我们在下面的示例中所做的那样:
该类的第二个方法是 repr 方法,它告诉 Python 在每次调用该类的一个实例时打印一条预定的语句,如上图所示。
想知道哪个是具体方法的功能?参考之前介绍的帮助功能。由于内置的类包含了帮助用户理解每个方法或属性背后的直觉的指南,我们也可以在我们自己的类、方法和函数上这样做。我们可以通过添加一个文档字符串来实现。
Docstring 是解释某事的简短文本。
一旦将文档包含到类和对象中,您将获得更多关于所创建方法的信息,这将促进代码的可重用性并帮助其他用户理解它。请记住,docstring 必须始终在它所记录的块的同一级别缩进。
5.代码重用
面向对象编程的另一个重要方面是继承。就像人有父母、祖父母等等一样,对象也有祖先。继承的原则允许程序员在概念之间建立关系,并将它们组合在一起。特别是,这允许我们通过一般化我们的代码来减少代码重复。
例如,除了我们已经创建的汽车,或者除了我的狗之外的其他宠物,我们如何定义“其他交通工具”的表示?这种分组特性允许我们创建共享现有类的一些属性(但不是全部属性)的其他类,以便在不重写现有代码的情况下添加其他类似的实例。
在 Python 中,我们在类声明中使用括号来显示一个继承关系。在上面的运输示例中,我使用 Python 的语法告诉计算机汽车和火车都继承自 MyTransports 类。因此,它们自动具有相同的构造函数,用于设置颜色和品牌属性。
通过继承技术,我们可以使用 transportation 类来存储适用于所有可用传输方式的信息,并将汽车或火车的特定属性保存在它们自己的类中。您可以将 MyTransports 类视为父类,将 Car 和 Train 类视为兄弟类。
与您在 IT 部门执行的任务更接近的一个例子是处理您公司员工的系统。您可能有一个名为雇员的类,该类可能具有诸如人员的全名、公司系统中使用的用户名、雇员所属的组等属性。系统还可以有一个经理类,它也是一个雇员,但是有与之相关的附加信息,比如向特定经理报告的雇员。
结论
在 python 这样的面向对象语言中,现实世界的概念是由类来表示的。类的实例通常被称为对象。这样的对象有用来存储关于它们的信息的属性,我们可以通过调用它们的方法使对象工作。这篇文章的目的是让您清楚地了解 O.O.P .对程序员来说有多有用,因为它允许我们对现实世界的概念进行建模。
感谢您花时间阅读我的文章!如果您有任何问题或想法要分享,请随时通过我的电子邮件联系我,或者您可以在以下社交网络中找到我以了解更多相关内容:
如果您对更深入了解 Python 编程的更多相关文章感兴趣,请考虑以下内容:
理解 Python 中的循环:
读完这篇文章后,你将不再有机会被 Loops 抛出。
towardsdatascience.com](/understand-loops-in-python-with-one-article-bace2ddba789)
用 Python 创建惊人的可视化效果:
我准备了一份详尽的指南来展示漂亮的可视化效果,以增强度量、KPI、预测和其他…
towardsdatascience.com](/business-intelligence-visualizations-with-python-1d2d30ce8bd9)
创建股票价格模拟器:
几何布朗运动过程的简单应用来模拟股票价格。
towardsdatascience.com](/create-a-stock-price-simulator-with-python-b08a184f197d)
使用 Python 的电子商务数据科学:
我为电子商务行业准备了一份广泛的数据科学应用指南。
towardsdatascience.com](/data-science-for-e-commerce-with-python-a0a97dd7721d)
了解熊猫指数
为了有效地使用 Pandas,请忽略它的文档并了解索引的真相
Damian Patkowski 在 Unsplash 上拍摄的照片
Python Pandas 库是一个很好的数据操作工具。然而,只有当你理解熊猫索引时,它才是有效的。Pandas 索引是在几秒钟而不是几分钟或几小时内访问和连接行的关键。
指数
像 Python 字典(或关系数据库的索引)一样,Pandas 索引提供了一种将键转化为值的快速方法。例如,我们可以创建一个索引为alpha
的数据帧:
然后将键b
转到感兴趣的行。
但是熊猫指数是一个什么样的东西呢?文档说索引是一个
不可变 n 数组实现一个有序的、可切片的集合(增加了重点)
换句话说,一种数学集合。回想一下,数学集合有两个重要属性:
- 没有重复的元素
- 元素是无序的
但是现在,看第二个例子:
我们又把alpha
栏目变成了索引。然而,元素x
出现了两次,检索到的行遵循两个x
的顺序。
- 元素可以重复
- 元素是有序的
因此,与熊猫文献相反,熊猫指数不是一个数学集合。取而代之的是一种列表。具体来说,熊猫指数是
- 可散列元素的(一种)列表,其中
- 可以快速找到元素的位置。
有了这些知识,我们可以很容易地理解索引的基础,从它们的创建、删除和操作开始。
操纵索引
上面的例子展示了如何用.set_index()
将一个列转换成索引。我们可以用.reset_index()
将索引变回列:
让我们把索引放回去,然后看看索引中的所有元素。属性为.index.values
。正如所料,元素是一种列表,具体来说,是一个 NumPy 数组。
我们还希望能够快速找到对应于任何索引元素的行号。方法是.index.get_loc()
。结果将是一个整数或布尔数组,具体取决于行数。
行访问
访问带有索引元素的行的主要方法是.loc[…]
(注意方括号),其中的输入可以是:
- 单一元素
- 元素列表
- 元素切片
行将按照它们在输入中出现的顺序输出。这个例子展示了每一种输入。
注意,与 Python 的其他部分不同, start:stop 片段包含了 stop 值。
连接行
最后,让我们看看连接两个数据框架。规则是:
- 左边的数据帧不需要索引,但是右边的需要。
- 在连接的
on
输入中给出感兴趣的左列。
在这个例子中,我们将使用join
向 dataframe 添加一个“score”列。这是左边的数据框。它没有索引。
正确的数据帧需要一个索引,但它可以被命名为任何名称。在这里我们称之为alpha2
。
我们用左连接将两个数据帧结合起来。我们使用第一个数据帧中的列alpha
和第二个数据帧中的索引。结果是一个带有分数列的新数据帧。
结论
我们已经看到,与文献相反,熊猫指数不是一个数学集合。相反,它是一种可以快速找到任何元素位置的列表。
理解了这一点,就很容易理解如何创建、删除和操作索引。然后,我们可以使用索引来快速访问和连接行。
下一步是什么?有了这个基础,接下来您应该学习从多个列创建索引,对索引应用类似集合的操作符,以及有效地删除行。(令人惊讶的是,熊猫分组排序不需要也不使用索引。)
了解 Python 中的 slots。
改进 Python 代码的简单方法
当我们从一个类中创建一个对象时,该对象的属性将被存储在一个名为__dict__
的字典中。我们使用这个字典来获取和设置属性。它允许我们在创建对象后动态地创建新的属性。
让我们创建一个简单的类Article
,它最初有两个属性date
和writer
。如果我们打印出对象的__dict__
,我们将得到每个属性的键和值。同时,我们也打印出以后需要的类的__dict__
。之后,一个新的属性reviewer
被添加到对象中,我们可以在更新后的__dict__
中看到它。
够好吗?
在我们找到更好的解决方案之前,我们不能说这不好。Dictionary 在 Python 中非常强大,但是在创建成千上万的对象时,我们可能会面临一些问题:
- 字典需要记忆。数百万个对象肯定会耗尽内存。
- 字典实际上是一个哈希表。hash map 中 get/set 的时间复杂度最坏情况为 O(n)。
__ 插槽 _ _ 解决方案
来自 Python 文档 : slots 允许我们显式声明数据成员(如属性)和拒绝 dict 和 weakref(除非在 slots 中显式声明或在父代中可用。)
那么,这和我提到的问题有什么关系呢?
让我们创建一个类ArticleWithSlots
。两个类之间的唯一区别是额外的字段__slots__
。
__slots__
是在类级别上创建的,这意味着如果我们打印ArticleWithSlots.__dict__
,我们应该能够看到它。此外,我们还看到了类级别上的两个额外属性,date: <member 'date' ..>
和writer: <member 'writer' ..>
,它们属于类 member_descriptor 。
Python 中的描述符是什么?
在讨论描述符之前,我们应该理解 Python 中访问属性的默认行为。当你执行article.writer
时,Python 将调用方法__getattribute__()
,在__dict__
、self.__dict__["writer"]
中进行查找并返回值。
如果查找键是具有描述符方法之一的对象,那么默认行为将被描述符方法覆盖。
ArticleWithSlots 类的屏幕截图
描述符方法包括__get__()
、__set__()
和__delete__()
。描述符只是一个 Python 对象,它实现了至少一个描述符方法。
__slots__
通过描述符方法的实现,自动为每个属性创建一个描述符。你可以在截图中找到它们。意味着对象将使用__get__()
、__set__()
和__delete__()
与属性交互,而不是默认行为。
据吉多·范·罗苏姆,__get__()
,__set__()
的实现用数组代替字典,完全用 C 实现,效率高。
slots 具有更快的属性访问速度
在下面的代码中,我比较了Article
和ArticleWithSlots
的对象创建时间和属性访问时间。__slots__
快 10%左右。
__slots__
有稍微好一点的性能是因为的时间复杂度的 get/set 操作在最坏的情况下比一个链表中的字典要快。由于O(n)
只发生在最坏的情况下,我们大多数时候不会注意到差异,尤其是当你有少量数据的时候。
数据来源:wiki.python.org
slots 减少了 RAM 的使用
由于属性可以作为数据成员(比如属性)来访问,所以没有必要将它们存储在字典__dict__
中。其实,__slots__
根本就否定了__dict__
的创作。所以如果打印article_slots.__dict__
,会得到 AttributeError 异常。
这种行为减少了对象的 RAM 使用。我将使用 pympler 比较article
和article_slots
的大小。不使用sys.getsizeof()
的原因是getsizeof()
不包括被引用对象的大小。但是,__dict__
是一个被引用的对象,在getsizeof()
中将被忽略。
原来article_slots
比节省 50%以上的内存。哇,如此惊人的进步!
这么好的性能是因为article_slots
没有__dict__
属性,实际上节省了很多内存。
什么时候使用和不使用 slots?
到目前为止,看起来__slots__
是一个非常好的特性。能不能每节课都加?
答案是否定的!显然,有一些权衡。
固定属性
使用__dict__
的原因之一是它在创建对象后的灵活性,在这里你可以添加新的属性。然而,__slots__
将在您创建类时修复属性。因此,以后不可能添加新的属性。
但是…
在某些情况下,你想利用__slots__
,也有在运行时添加新属性的灵活性。你可以通过在__slots__
中添加__dict__
作为属性来实现。只有新添加的属性会出现在__dict__
中。当您的类有 10 个以上的固定属性,并且您希望以后有 1 个或 2 个动态属性时,这将非常有用。
继承
如果你想继承一个包含__slots__
的类,你不必在子类中重复这些属性。否则,子类会占用更多的空间。此外,重复的属性在父类中将是不可访问的。
当您继承一个命名的元组时,情况也是如此。你不需要在子类中重复属性。如果你想了解更多关于 NamedTuple 的信息,你可以阅读我的文章专门讨论这个话题。
你也可以在子类中添加__dict__
属性。或者,你不把__slots__
放在子类中,它默认会有__dict__
。
如果你继承了一个没有__slots__
的类,那么子类将包含__dict__
。
结论
希望你已经理解了什么是__slots__
以及实现的一些细节。在文章的最后,我想分享来自我自己的经验和互联网的利弊(链接在参考资料中)。
优点
当您对内存使用有压力时,__slots__
绝对有用。只需一行代码就可以非常容易地添加或删除。在__slots__
中将__dict__
作为一个属性的可能性给了开发人员更多的灵活性来管理属性,同时兼顾性能。
缺点
你需要清楚你在做什么,你想用__slots__
实现什么,特别是当用它继承一个类的时候。继承的顺序、属性名称会对性能产生巨大的影响。
不能用非空的__slots__
继承int
、bytes
、tuple
等内置类型。此外,你不能给__slots__
中的属性赋予默认值。这是因为这些属性应该是描述符。相反,您可以在__init__()
中指定默认值。
我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
参考
特殊属性 slots 允许您显式地声明您希望对象具有哪些实例属性…
stackoverflow.com](https://stackoverflow.com/questions/472000/usage-of-slots) [## 新型课堂的内幕
警告,这篇文章很长,而且非常专业。]从表面上看,新型的类看起来非常类似于…
python-history.blogspot.com](http://python-history.blogspot.com/2010/06/inside-story-on-new-style-classes.html) [## 时间复杂性- Python Wiki
本页记录了当前 CPython 中各种操作的时间复杂度(也称为“Big O”或“Big Oh”)。其他…
wiki.python.org](https://wiki.python.org/moin/TimeComplexity) [## 3.数据模型- Python 3.8.3 文档
对象是 Python 对数据的抽象。Python 程序中的所有数据都由对象或关系来表示…
docs.python.org](https://docs.python.org/3/reference/datamodel.html#slots)
理解火花,就好像是你设计的一样
从简单的功能到灵活的分布式框架。
为什么关心星火?
在当前数据领域可用的框架中,就采用和交付而言,只有少数几个达到了 Spark 的地位。框架已经成为明显的赢家之一,特别是在数据工程领域。
如果你正在看这篇文章,说明你已经明白了前一段背后的原因,所以我们直接跳到正题。
为什么关心 Spark 内部?
有人可能会说,我们不必为了驾驶汽车而去理解发动机是如何工作的,这是事实。然而,有人可能会说,了解发动机会让你成为一名更好的飞行员,因为你能够了解整辆车的能力、局限性和最终问题。
根据同样的原理,您不必理解 Spark 的内部机制就可以使用它的 API。但是,如果您这样做了,从低性能到隐藏的 bug 的许多痛苦将会减轻。此外,你将掌握贯穿整个分布式系统领域的概念。
进场
在我看来,学习有两种方式:知识和技术。前者与正规的知识获取有关,通过书本、结构化课程等。它更关注于什么。后者与工艺有关,而“边做边学”则更侧重于如何。这就是我们现在要走的路。
我们将从一个简单的问题开始,每个初学者都可以解决这个问题,并对其进行改进,以证明 Spark 的架构设计是正确的。在这个过程中,我们还将了解 HDFS (部分称为 Hadoop),因为它是一个与 Spark 配合得非常好的平台。
我们将是语言不可知的,所以所有的代码实际上都是伪代码。
问题
你被录用了,并被分配了一个简单的任务:数一数你的数组中有多少个偶数。
您将从本地文件系统中存储的 CSV 文件中读取该数组。不用想太多,你可能会得到下面的一大块:
新要求 1
你的客户对之前解决方案的巨大成功感到高兴,人们现在认为他们可以把每个问题都扔给你,所以他们要求你也计算这些偶数的平均值。
你肯定知道坚实的原则,特别是单一责任原则,它说一个类或方法应该只有一个改变的理由。然而,你决定打破规则,只是实现如下:
新要求 2
既然你这么快,人们又提出了另一个要求:还要返回所有偶数的和。
这时,你不仅开始考虑 T2 坚实的原则,也开始考虑事情的发展方式。你知道,通常,当一件事情发生一次并不意味着它会发生两次,但如果它发生两次,第三次就在眼前。所以你开始考虑实现一些更容易扩展的东西,你还记得面向对象编程中的封装概念。
此外,如果您捕获了适当的抽象,那么当另一个需求出现时,您甚至不必更改您的实现。
一组抽象来管理它们
你首先考虑到,如果他们让你数偶数,他们很可能会进一步问你奇数,或者低于或高于某个值,或者在某个范围内,等等。因此,即使你是应用 YAGNI ( 你不会需要它)的专家,你也决定实现一些支持所有这些情况的东西。
最后,您得出结论,所有这些操作都与从数组中过滤值有关,因此,您没有对每个可能的过滤器进行编码,而是决定提供一个接受过滤条件的过滤函数。
此外,为了简化设计,您决定在每次对对象调用操作时更改对象的状态。
迎接新的挑战
你做到了。现在,您不仅实现了所有的需求,还摆脱了涉及从数组中过滤值的新需求。如果您的客户现在想要奇数,而不是偶数,他们唯一要做的就是向 filter 方法传递一个新条件,这样就完成了。太神奇了!但是,您等待的新需求刚刚到来:他们现在需要您处理一个 3 TB 的阵列。
你考虑放弃。你自己的硬盘只有 500 GB 大,所以你需要 6 台像你一样的机器专门用来存储那个文件。但你的客户喜欢你,也很有说服力,在理所应当的加薪后,他们还承诺为你提供不是 6 台而是 30 台新机器来解决问题。
划分
有了 30 台新机器,你开始考虑如何解决这个问题。一台机器不可能包含整个文件,所以你必须把它分割成更小的块,以适应每个新的硬盘。此外,您考虑到既然您有足够的资源,那么您也可以将同一个片存储在多台机器上,作为备份。也许每个切片有两份拷贝,这意味着你可以在三个不同的地方找到那个切片。
您格式化硬盘并开始复制文件,在此过程中,您认为将所有切片保存在每台机器的同一规范父文件夹中是一个好主意,并且为每个切片添加一个标识符前缀,该标识符与它属于更大文件的哪个部分相关。您还认为在至少两台计算机中有另一个目录也是一个好主意,该目录包含元数据,这些元数据说明哪些目录包含存储片,哪些计算机包含每个存储片 id 的备份。
因为你的一些机器只包含数据,一些只包含给事物指明方向和名称的元数据,你决定把前一个称为数据机器,后一个称为机器。但是因为您实际上是在创建一个网络,所以将机器称为节点更合适,所以您将数据机器命名为数据节点,将元数据机器命名为名称节点。
在给事物命名的过程中,你会意识到 slice 与蛋糕和奶酪的关联要大于与数据块的关联。你觉得很有灵感和创造力,所以你决定给这些切片取一个更好的名字:分区。所以,无论你的程序最终变成什么,它都会处理被分成分区的整个文件。
在所有这些命名和决定之后,您会得到这样的结果:
您的第一个分布式文件系统
征服
现在,您已经将文件划分为跨越一组节点的分区(从现在开始,我们将创造性地称之为集群),备份和元数据帮助您的程序找到每个分区及其备份。既然移动分区没有任何意义,那么问题就变成了:在每台机器上执行同一段代码并得到一个结果?
每次需要运行程序时,是否应该将整个程序发送到每台机器上?或者你应该有一些已经存在的片段,这样你只需要发送你的客户写的部分?后者听起来更好,所以你跟着它走。在这个过程中,第一个要求是让您的 ArrayOperator 类在每台机器上都可用,并且只发送由 main 方法指定的部分。
您还希望在尽可能靠近数据的地方运行代码,因此您的数据节点也必须运行您的程序。从这个角度来看,节点不仅存储数据,还执行实际工作,因此您决定将它们称为 workers 。
代码的某些部分也可以并行运行。例如,对于上面的程序,您可以并行执行 average()、、sum() 和 size() ,因为它们彼此独立。为了实现这一点,您的工作人员需要支持独立的执行行,因此您决定将每个工作人员转换为某种守护进程,该守护进程将产生独立执行任务的新进程(同时,您意识到 task 是一个通用名称,足以指代可以独立执行的每个单元)。既然你仍然受到启发,你决定创造性地将执行任务的过程称为执行者。
现在,您所要做的就是设计您的 main 方法——它可以访问您的客户端代码——使它能够将您的客户端代码分离成将组成作业的任务,然后询问 name 节点哪些数据节点包含文件的每个分区,然后将任务并行发送到 worker 机器,worker 机器将准备启动将执行任务并返回结果的执行器。因为这段代码将驱动整个事情,你,仍然被同样的创造力所祝福,决定称它为驱动。
你的司机也需要知道如何把所有的结果放在一起。在这种情况下,它需要将从每个工人那里收到的所有总和加起来。但考虑到目前为止的进展,这只是小菜一碟。
总的来说,你的司机将会协调完成任务。这又是你的想象。哪个名称比作业更适合描述一组任务?
你美丽的工程作品
造破
几个晚上之后,你终于可以让所有的部分一起运行了。多么令人印象深刻的壮举!你测试它,一切都像预期的那样工作。你渴望看到一个演示,而你的客户在投入大量资金后,也同样渴望看到这个演示。
你通过表扬自己开始演示,这是应该的,然后继续解释架构。你的客户会更加兴奋。你运行程序。一切都崩溃了,因为你的 5 台机器离线了,2 台是因为内核崩溃,2 台是因为硬盘故障,还有 1 台是因为一个未测试的特性导致了一个错误。每个人都开始哭泣,除了你。你的客户失去了信心,但你的信心却坚如磐石。你实际上再次表扬了自己,因为你已经想通了所有的事情。这些备份并非出于巧合。
你保证一周后会有新的演示。你的客户离开的时候很生气,有点悲伤,但是你保持冷静。
“少就是少,不是多”,2020 年前后备份
你猜对了。由于每个分区包含两个其他副本,并且您有 28 台机器(请记住,您为名称节点保留了 2 台),如果 5 台机器的故障导致整个集群停机,您将非常不幸。
但是如何利用冗余呢?你可以确定的一件事是,它应该从驱动程序端开始,因为这是与所有节点通信的部分。如果您有一个失败的节点,它将首先被驱动程序注意到。由于在作业开始时,驱动程序已经与名称节点取得联系,以查找分区的位置,因此它可能还会向名称节点询问故障 worker/data 节点中所有副本的位置。有了这些信息,它只需重新发送要在副本上执行的任务,您就大功告成了!
使用前面的方法,您可以用一种弹性的方式让分布式处理分布式数据。
你去争取吧。
重新开始
你打电话给你的客户,要求一个新的演示。他们假装仍然很沮丧,但几乎无法掩饰他们的兴奋。他们来看你,进入这个地方说一些关于上次的笑话。你只看到“蓝屏”,却不太关心笑点。
在开始之前,您做了一些令人震惊的事情:您要求他们随机关闭两个工作人员/数据节点。他们看起来很惊讶,但进入了状态(看到他们带着奸诈的微笑随机选择机器,试图比你更聪明,这很有趣)。
少了两个节点,您就可以开始演示了,效果非常好。他们哭了,但这一次的眼泪不一样。他们让你振作起来,为不相信而道歉,给你加薪,当然,还带来了新的要求:数组现在将包含具有多种属性的对象,而不是数字。更具体地说,记录将包含姓名、年龄和工资,他们想知道被称为费利佩的人的平均年龄和最高工资是多少。他们还想保存结果,以便以后可以访问而无需重新处理。
你一点都不惊讶。
蛋糕上的樱桃
这个时候你不用多想。你一直在玩抽象,所以现在只需要再向上移动一级。
您偏离了之前的设计,并像这样进行了更改:
有了新的设计,您现在可以处理任何类型的记录(这就是为什么您将其名称改为 GeneralOperator )。
这真是太神奇了!想想吧。你有一个系统,可以以分布式和弹性的方式读取、写入和处理任何类型的数据集。更自由地说,你可以宣称你有一个支持处理任何种类的弹性和分布式 数据集的框架。
你感觉到了掌握在你手中的力量,但是你认为你的魔法的核心 GeneralOperator 没有一个足够吸引人的名字。或者至少它不是非常自明的。但是你没有更好的想法,所以你决定称它为弹性和分布式数据集读取器、写入器和处理器。但是那太大了。所以可能是首字母缩写,比如 RDDRWP?哎哟,更惨。那只有 RDD 的呢?容易发音和发音,以防有人请你翻译。听起来不错,你完成了。
TLDR;
以下是您所做的工作:
1.您已经设计了一个以分布式方式存储复制数据的基础设施分区,它由保存数据的数据节点和包含元数据的名称节点组成(这一对难道不应该有自己的名称吗?那么 HDFS 呢?)
2.您创建了一个名为弹性分布式数据集(简称 RDD )的结构,可以读取、写入和处理存储在 Hadoop 集群中的数据。
3.您已经构建了一个基础架构,通过控制给定节点上的执行的工作器和实际执行任务的执行器,在分布式分区上并行执行任务。
4.您设计了一个驱动程序应用程序,它将客户提供的作业分解成多个任务,与命名节点对话以找出分区的位置,并将任务发送给远程工作人员。
伙计,你真棒!但是你的创造难道不应该有一个好听的名字吗?有太多的想法,一个接一个的火花。耶,火花!听起来像个名字!
你可以这样推销它
按比例放大
你创造的这个东西当然有很大的价值,但也许它有一个有点陡峭的学习曲线。另一方面,长期以来(可能太久了),处理数据的语言选择是结构化查询语言,或者 SQL 。把这种能力带入你的火花怎么样?
让我们与客户聊天。
重要的
以上是 Spark 组件的一个非常简化的视图,其主要目的是提供对 Spark 架构的一个总体把握。与催化剂、调度、转换类型、洗牌、计划、资源分配、专门的 API 方法等相关的元素被有意省略,以使文本更简单。他们将在以后的作品中接触。
进一步阅读
关于 Spark:https://data-flair . training/blogs/Apache-Spark-ecosystem-components/
关于 rdd:https://spark . Apache . org/docs/latest/rdd-programming-guide . html
在 OOP 上:https://en.wikipedia.org/wiki/Object-oriented_programming
关于 S.O.L.I.D 原则:https://scotch . io/bar-talk/S-o-l-I-d-first-first-five-principles of-object-oriented-design
https://martinfowler.com/bliki/Yagni.htmlYAGNI
使用 Python 中的单变量和多变量图表和绘图理解数据
学习一些使用 python 中的医疗数据来理解和可视化数据的技术
单变量数据分析是最简单的数据分析形式。顾名思义,它处理一个变量。它没有发现变量之间的因果或关系。单变量数据分析的目的是总结和描述一个数据或一个变量。如果包含两个变量,它就变成了二元变量。在本文中,我们将使用单变量和双变量数据分析来理解和可视化一些数据。在一些实践中,我们也将包括三个变量。所有的信息只适用于本文中使用的特定数据集。
了解数据集
我们将使用来自 Kaggle 的心脏数据集。首先导入包和数据集。
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from statsmodels import api as sm
import numpy as npdf = pd.read_csv("Heart.csv")
让我们清楚地看到列名:
df.columns
#Output:
Index(['Unnamed: 0', 'Age', 'Sex', 'ChestPain', 'RestBP', 'Chol', 'Fbs', 'RestECG', 'MaxHR', 'ExAng', 'Oldpeak', 'Slope', 'Ca', 'Thal', 'AHD'], dtype='object')
你现在可能不明白每一列是什么意思。在这篇文章中,我将只使用几个列,并且我将继续解释列名的含义。
解决一些问题
- 找出不同类型血液疾病的人口比例。
我们会在“Thal”栏中找到。在这里,“地中海贫血”是指一种叫做地中海贫血的血液疾病。Pandas 中有一个名为“value_counts”的函数,用于计算一个系列中每个类别的值。
x = df.Thal.value_counts()
x
这些是患有正常、可逆和固定疾病的人数。现在,将它们分别除以总人口,得出人口比例。
x / x.sum()
如果你注意到,这些比例加起来不等于 1。在这次计算中,我们漏掉了一件事。可能会有一些值。用“缺失”填充这些空格,然后再次计算比例。
df["Thal"] = df.Thal.fillna("Missing")
x = df.Thal.value_counts()
x / x.sum()
所以,有一些缺失的值。在这个数据集中,54.79%的人患有正常地中海贫血。下一个大的是 38.16%,他们有可逆的地中海贫血。
2.找出胆固醇数据的最小值、最大值、平均值和标准偏差。
有一个功能叫做‘描述’。让我们利用这一点。我们将获得我们需要的所有信息以及一些其他有用的参数,这些参数将帮助我们更好地理解数据。
所以,我们得到了一些额外的有用参数。人口总数是 303。我们不打算在本文中使用它。但它在统计分析中很重要。尤其是在推断统计学中。“describe”函数还返回 25%、50%和 75%的百分比数据,这些数据提供了数据分布的概念。
3.绘制胆固醇数据的分布图。
sns.distplot(df.Chol.dropna())
分布稍微向右倾斜,有一些异常值。
4.求 RestBP(静息血压)的平均值。然后,计算 RestBP 高于平均 RestBP 的人口比例。
mean_rbp = df.RestBP.dropna().mean()
平均 RestBP 为 131.69。首先,找到 RestBP 大于均值 RestBP 的数据集。除以总数据集的长度。
len(df[df["RestBP"] > mean_rbp])/len(df)
结果是 0.44 或 44%。
5.绘制胆固醇数据与年龄组对比图,观察不同年龄组人群中胆固醇水平的差异。
这是解决方案。在数据集中创建一个新列,该列将返回不同年龄组的人数。
df["agegrp"]=pd.cut(df.Age, [29,40,50,60,70,80])
现在,制作方框图。将年龄组放在 x 轴上,胆固醇水平放在 y 轴上。
plt.figure(figsize=(12,5))
sns.boxplot(x = "agegrp", y = "Chol", data=df)
箱线图显示胆固醇随着年龄的增加而增加。检查性别是否起作用是一个好主意。如果不同性别的人胆固醇水平不同。在我们的性别专栏中,数字 0 和 1 分别代表女性和男性。我们将创建一个新列,用“男性”和“女性”替换 0 或 1。
df["Sex1"] = df.Sex.replace({1: "Male", 0: "Female"})
plt.figure(figsize=(12, 4))
sns.boxplot(x = "agegrp", y = "Chol", hue = "Sex1", data=df)
总体而言,该数据集中的女性人群胆固醇水平较高。在 29 岁到 40 岁的年龄组,情况就不同了。在 70 至 80 岁的年龄组中,胆固醇水平仅见于女性人口中。这并不意味着该年龄段的男性没有胆固醇。在我们的数据集中,该年龄组的男性人口不足。如果我们按年龄绘制男性和女性人口图,将有助于理解。
sns.boxplot(x = "Sex1", y = "Age", data=df)
6.制作一个图表,显示每个年龄组中患有各种类型胸痛的人数。
df.groupby('agegrp')["ChestPain"].value_counts().unstack()
对于每种类型的胸痛,最大值出现在 50 至 60 岁的年龄段。可能是因为我们的数据集中该年龄组的人数最多。看上面的图片。
7.制作与之前练习相同的图表,并添加性别变量。按性别区分数字。
dx = df.dropna().groupby(["agegrp", "Sex1"])["ChestPain"].value_counts().unstack()
8.在前一张图表中,列出同一组中每种类型胸痛的人群比例。
dx = dx.apply(lambda x: x/x.sum(), axis=1)
那是最后一次练习。这些是制作单变量和多变量图表和绘图的一些技巧。我希望这有所帮助。在这里,我有一些相关文章的链接:
- 通过直方图和箱线图了解数据
用 ML 解释器理解机器学习黑盒
一个自动解释 XGBoost 等算法决策的 web 应用程序
让模型来管理这个世界并做出从雇佣到刑事司法的决策是有危险的。虽然拥有既可解释又准确的模型是理想的,但许多流行且强大的算法仍然是黑盒。
其中有高性能的树集成模型,如 lightGBM,XGBoost,random forest。了解他们的内部运作会带来很多好处,包括透明、信任、合规和公平。否则,人们可能不得不求助于更易解释的原始模型。
为了让解释更容易,我构建了一个 web 应用 ML-interpreter 。
在深入详细的功能之前,这里有一个关于可解释性的初级读本。
解释框架的简要概述
现在有很多可解释性框架,包括 Python 包 LIME 、 SHAP 、 ELI5 、 Skater ,以及不太知名的如 ALIBI 、R 包 Dalex 以及 SHAP 和 LIME 的 R 包装器。一开始,处理许多不同框架的机制、语法和用法可能会令人不知所措,这促使我开发了这个应用程序。以下是一些关键概念。
冲击与方向
为了解释一个模型,我们通常想知道(1)特性对结果的影响和(2)影响的方向。
图片来源: SHAP
全球对本地
我们还想知道:
- 哪些功能总体影响最大(全局解释)
- 如何批准每个决策点(当地解释)
大多数框架既可以进行全局解释,也可以进行局部解释,有些侧重于局部层次,比如 LIME。
与型号无关与与型号相关
像 SHAP 这样的框架是模型不可知的。其他人专门研究特定的模型,如 ELI5,它主要涵盖基于树的模型,但也可以用于解释 sklearn 线性模型和文本/图像用例。
这些框架是如何工作的
排列重要性
对于全局重要性,一种常用的方法是排列重要性。通过改变特定特性的值并观察它对模型性能的影响程度,可以推断出该特性的重要性。这种方法被认为比某些包中默认的特征重要性度量更一致。你可以在这里阅读更多关于为什么的内容。
图片来源:ML Kaggle 的可解释性
代理模型
对于局部解释,可以训练简单的可解释模型(如决策树)作为代理,然后使用该代理模型将原始数据与作为解释目标的黑盒模型的预测进行拟合,并使用该简单模型进行解释。
博弈论
另一种方法是基于博弈论的沙普利值。单个特征的影响可以加起来解释为什么预测与基线不同。你可以在这篇博客帖子或这篇 arXiv 论文中了解更多关于 SHAP 的树解释器是如何工作的。
接下来,我们将使用 app 进行一些解读。
ML 解释程序
该应用程序由 4 个主要部分组成:用户选择、分类报告、每个决策点的全局解释和局部解释。
人们可以选择一个演示数据或更新一个小的表格 csv,运行一个分类器(randomforest,XGBoost,lightGBM),并在可解释性框架之间进行选择。treeSHAP 和 ELI5,因为它们覆盖了全局和局部解释,并且很好地处理了树集合模型。
示例
现在,我们将在 UCI 的成人收入数据集(包含在应用程序的演示数据中)上测试它,该数据集根据人口普查预测个人年收入超过 5 万美元的概率。首先我们可以选择一个内置模型(比如 lightGBM),选择一个框架(SHAP)。
视察模特表演
我们可以从查看分类报告和混淆矩阵开始。请注意,这是没有任何数据清理或特征工程的输出。因此,如果预先对数据进行预处理,比如删除零方差或 id 列(如果有的话),结果可能会更好。
理解全局解释
当我们对模型的结果满意后,我们可以检查最重要的特征并发现关系、教育和年龄对收入的影响最大。
全局解释
放大局部解释
然后,为了检查这些特征如何影响收入,我们可以使用滑块放大到单个数据点。你可以在应用中阅读更多关于如何阅读这个情节的信息,或者点击这里。
示例 1
在这个例子 1 中,这个人是一个 21 岁的人,每周只工作 8 个小时,他被正确地归入低收入类别。
示例 2
在另一个例子 2 中,被归类为高收入预算的人工作了相当长的时间,已婚并具有高学历,但是位置似乎降低了机会。
示例 3
在最后一个例子中,这个人工作了相当长的时间,但是许多因素都有负面影响。人们可以从最长的箭头看到最重要的因素。如果这个因素不合理,就应该重新处理数据,找到混淆因素或者进行调试。
如果我们转而使用 ELI5 框架,我们可以以表格形式查看不同的视图,总结出对个人决策影响最大的因素。
这是一个自动解释的应用程序,它运行一个选择的算法,显示模型的性能,并指出是什么使模型进行预测。
如果你对如何改进它有任何想法/建议,这里有一个反馈表格和到演示应用和 Github 的链接。
一路上的挑战
- 速度
构建这一框架的主要挑战是可解释性框架会变得计算密集型。像 SHAP 这样的软件包有 C++加速,而其他的需要更长的计算时间。一些分类器也比其他分类器花费更长的时间,数据大小加剧了这种情况,尽管人们可以首先尝试数据的样本。
最初,我将 PDPbox 中的 PDP/ICE 图作为图之一,显示每个特性如何随结果变化,但在其他计算之上呈现之前计算 20 秒的图表并不理想,所以最终我将它作为可选图表移动,可以通过选择复选框按需查看。
另一个选择是 ALEplot (最初是一个 R 包),它应该更快更好(可以处理 PDP 不能处理的相关特性),当其羽翼未丰的 python 版本稳定下来时,这可能会很方便。SHAP 也有自己版本的部分相关图,显示散点图中的每个数据点,但有时数据点只是相互遮挡(尽管其局部解释图超级棒)。
2.翻译口译员
另一个挑战是这些框架没有一个是完全不言自明的。感谢几位 ML/DS 人员的反馈,他们友好地帮助了初始测试,我在每个部分添加了操作按钮和链接来详细解释输出。
学习
- 我开始理解不同可解释性框架的复杂性。虽然重点是用树集成模型进行分类,但这些框架中的许多可以应用于更广泛的用例,包括回归、文本和图像。拥有黑盒中的可解释性可以为我们提供许多见解,但它可能不是万灵药,仍然需要谨慎使用。
- 通过这个项目,我使用了 Atair 进行绘图,并发现它的声明性语法非常简洁,这让我想起了 ggplot2 中的图形语法。现在为了保持功能简单,我没有用图表过载它。
- 使用构建和部署 ML 应用程序简化了。我最近在这里和这里写了详细的操作方法。
后续步骤
口译组
与重要特性的俯视图相比,理解单个决策点是相当大的进步。然而,更有趣和实用的是了解模型对特定群体的预测,基于输入要素或输出类别进行聚类,而交互式应用程序是实现这一点的好方法。我还没有找到一个适合解释集群的框架。这对于评估模型的公平性也有积极的意义。
反光
在学习这些框架时,我注意到它们在架构和实现上有多么不同。例如,Eli5 以(1)表格数据框格式和(2)图表两种形式提供结果,尽管它们的图表也是彩色的 HTML 表格。SHAP 的方法相当不同——它直接生成高度紧凑的可视化。但是,您可以运行一些脚本来获得数据框格式的输出。
所有这些框架都可以归结为两个部分——解释算法(T0)和视觉表现(T2)两部分。他们的大多数图表都有相同的目标,比如特性重要性或部分相关性,但他们都使用不同的图表来呈现相同的见解。对于新用户来说,在解释释义时转换齿轮并不那么容易。我开始想,将解释算法和它们的视觉表示分离开来,并拥有一种通用设计语言用于机器学习解释是否会更好,这种语言与框架无关,并且可以使交流更容易。
资源
我参考了这些文章/书籍,发现它们信息量很大:
机器学习的可解释性由 Kaggle
关于解释机器学习的想法奥赖利
机器学习可解释性介绍作者 Patrick Hall 和 Navdeep Gill 在 H2o
如果你喜欢这篇文章,请分享。建议或反馈总是受欢迎的。
理解数据中的模式
导入数据后的下一步
威廉·艾文在 Unsplash 上的照片
在我之前的博客中,我简单解释了如何清理数据、执行 EDA(探索性数据分析)以及什么是基本特征工程。
比如说,你做了一个“read_csv”并导入了数据。现在,接下来呢?接下来要谈的重要事情是,我们如何理解和分析数据集中的各种模式。这将帮助我们解决几个问题:
a. 它将帮助我们了解当前数据中有多少个缺失值/重复值(这是我们未来模型的一个问题)——在前一篇文章 中解释过
b. 确定自变量(特征/属性)和因变量(目标)?如果没有目标(在大多数真实情况下),了解如何创建一个。这将基于你正在做的问题陈述,我将在以后的博客中用一些例子来讨论。
c. 了解每个特征/属性相对于我们的目标列以及相对于彼此的总体分布。
d. 有没有可以去掉的特征因为它们最终解释的是同一个东西!(将在下一篇博客中解决这个问题)
来源:作者
数据分布分析
要了解数据分布和关系,有很多 python 库(seaborn,plotly,matplotlib,sweetviz 等。),这样会让你的工作更轻松。在此阶段需要记住的一些事情是:
- 确定你的数字和分类变量。如果您认为 zipcode 的一列是数字型的,因为它有数字,那么您可能想回头详细阅读一下分类和数字属性!出于本文的目的,分类特征是那些包含有限数量的类别的特征,而数字属性是那些包含连续实数的特征。我发现这个链接同样非常有用!这一部分看起来很小,但在机器学习建模的后期起着重要的作用。
- 一旦你成功地识别了不同类型的变量,就该看看它们的模式和分布了。对于不同类型的变量,您可能希望使用不同的图表/解释来更好地理解它们。
- 分类特征 —这里有一些有用的基本统计数据——统计每个特征中的唯一值、每个特征中的频率分布、每个类别与目标列的关系等。你可以在这里使用很多图表,我使用饼状图(用于较小的类别),条形图用于更多的类别,或者有时只是一个普通的表格来进行快速分析。这完全取决于是什么让发行版更容易让你理解!
- 数值型特征—工资、年龄、温度等数值型属性。,为他们创建一个直方图,了解他们的分布。我的首选代码行是 df[' <列> ']。形容()。这让我可以快速分析一个属性的最小值-最大值-平均值等,而无需在编码上投入大量时间。这里有很多图表,你也可以用来分析数字特征!
- 为了分析目标列,查看数据分布—分类问题中 1&0 的条形图/饼图,或回归问题(连续数)的直方图/散点图。
需要记住的一点是,EDA(以及整个数据科学)非常直观。
你可能认为这个过程看起来像导入数据-> EDA ->特征工程->建模->结果,但实际上,这个过程是一个循环,一切都是相互连接的。
因此,即使您到达了建模阶段(在跨越了数据预处理阶段的所有障碍之后),您也必须回来分析更多的模式,创建/删除更多的特性,并再次进行建模!耐心是关键:)
有很多库提供了很多“奇特的”交互式图形,但是除了好看的图形,您应该选择一个有助于您很好地理解数据,并且易于向他人解释的图形。
现在你已经在文章末尾了,感谢你的阅读!如果有什么想让我补充/更改/地址的,请留言回复!😃
理解转置卷积
了解转置卷积的概念,从头开始构建自己的转置卷积层
生成对抗网络(GAN)是用于新数据生成的最先进的人工神经网络之一。它广泛应用于照片生成、照片编辑、人脸老化等领域。GAN 的核心是发生器和鉴别器。他们两个玩一个对抗性的游戏,在这个游戏中,生成器正在学习通过制作看起来非常像真实图像的假图像来欺骗鉴别器,而鉴别器正在学习更好地检测假图像。在理想情况下,在训练结束时,生成器可以生成看起来与真实图像完全一样的假图像,但鉴别器仍然可以发现它们是假的。
虽然卷积层在鉴别器中起着重要作用,但转置卷积层是生成器的主要构建模块。多亏了 TensorFlow API — Keras,构建 GAN 变成了一个非常方便的过程。然而,为参数设置正确的值,比如内核大小、步长和填充,需要我们理解转置卷积是如何工作的。在这本笔记本里,我想分享一些我个人对转置卷积的理解,希望能帮助你揭开其中的奥秘。在整个笔记本中,我将使用卷积作为比较,以更好地解释转置卷积。我还将向您展示我如何实现这些理解来构建我自己的卷积和转置卷积层,它们的行为就像 Keras 的 Conv2D 和 Conv2DTranspose 层的简单版本。笔记本由三部分组成:****
- 什么是转置卷积?
- Keras conv 2d transpose 中有哪些参数(内核大小、步幅、填充)?
- 从头开始构建我自己的 Conv2D 和 Conv2DTranspose 图层
第一节:什么是转置卷积?
我把转置卷积理解为卷积的反义词。在卷积层中,我们使用一种称为互相关的特殊操作(在机器学习中,该操作通常被称为卷积,因此这些层被称为“卷积层”)来计算输出值。该操作将输入层中所有相邻的数字加在一起,由一个卷积矩阵(内核)加权。例如,在下图中,输出值 55 通过输入层的 3x3 部分和 3x3 内核之间的元素乘法计算得出,并将所有结果相加:
对输入图层的 3x3 部分和 3x3 内核进行一次卷积运算(图片由作者提供)
没有任何填充,该操作将 4x4 矩阵转换为 2x2 矩阵。这看起来像有人从左向右投射光线,并通过一个孔(3x3 内核)投射一个对象(4x4 矩阵),产生一个更小的对象(2x2 矩阵)。现在,我们的问题是:如果我们想从 2x2 矩阵倒退到 4x4 矩阵呢?好吧,直观的方法是,我们只要把光向后投!在数学上,我们可以将输入层中的每个值乘以 3x3 内核,而不是乘以两个 3x3 矩阵,从而得到一个 3x3 矩阵。然后,我们根据输入层中的初始位置将它们组合在一起,并将重叠的值相加:
将输入层中的每个元素乘以内核中的每个值
将所有四个结果层组合在一起,并将重叠值相加(图片由作者提供)
这样,总是可以确定转置卷积运算的输出可以与前一个卷积运算的输入具有完全相同的形状,因为我们只是进行了完全相反的操作。但是,您可能会注意到号码没有恢复。因此,必须使用完全不同的核来恢复初始输入矩阵,并且该核可以通过训练来确定。
为了证明我的结果不仅仅是一些随机数,我通过 Keras 使用上述条件建立了卷积神经网络。从下面的代码可以看出,输出完全相同。
为什么“换位”?
现在你可能想知道:嘿,这看起来就像一个反向卷积。为什么命名为“转置”卷积?
说实话,我也不知道自己为什么要纠结这个问题,但是我做到了。我相信它被命名为“转置”卷积是有原因的。为了回答这个问题,我看了很多关于转置卷积的网上资源。一篇名为“转置卷积上采样”的文章帮了我大忙。在这篇文章中,作者 Naoki Shibuya 使用一个零填充卷积矩阵来表示卷积运算,而不是普通的方形卷积矩阵。本质上,在执行卷积变换时,我们可以将其表示为 4x16 矩阵,而不是将上述核表示为 3x3 矩阵。除了将上述输入表示为 4x4 矩阵,我们还可以将其表示为 16x1 向量:
将 3x3 内核表示为 4x16 补零卷积矩阵(图片由作者提供)
采用 4x16 矩阵的原因是:
- 4 行:总共,我们可以通过将一个 4x4 的输入矩阵拆分成四个 3x3 的矩阵来执行四次卷积;
- 16 列:输入矩阵将被转换成 16x1 的向量。为了执行矩阵乘法,它必须是 16 列。
将 4x4 输入矩阵表示为 16x1 向量(图片由作者提供)
这样,我们可以直接执行矩阵乘法来得到一个输出层。整形后的输出图层将与通过常规卷积运算得到的图层完全相同。
4x16 卷积矩阵和 16x1 输入向量之间的矩阵乘法(图片由作者提供)
最有趣的部分来了!当我们执行转置卷积运算时,我们只是简单地转置补零卷积矩阵,并将其乘以输入向量(这是卷积层的输出)。在下图中,中间阶段的四个彩色向量代表矩阵乘法的中间步骤:
16x4 转置卷积矩阵和 4x1 输入向量之间的矩阵乘法(图片由作者提供)
如果我们在中间阶段重新排列四个向量,我们将获得四个 4x4 矩阵,其数量与我们通过将 3x3 内核与输入层中的每个单独元素相乘而获得的 3x3 矩阵完全相同,多余的槽用零填充。这四个矩阵还可以进一步组合,以获得最终的 4x4 输出矩阵:
将 16×1 的中间向量重新排列成 4×4 的中间矩阵(图片由作者提供)
将所有四个中间矩阵组合在一起,产生最终结果(图片由作者提供)
因此,该操作被称为“转置”卷积,因为我们执行了完全相同的操作,只是我们转置了卷积矩阵!
第二节:Keras conv 2d transpose 中的参数(内核大小、步幅、填充)有哪些?
1.内核大小
在卷积中,内核大小影响着你在输入层中“投射”多少个数字以在输出层中形成一个数字。内核越大,使用的数字就越多,因此输出层中的每个数字都是输入层的更广泛的表示,并携带来自输入层的更多信息。但同时,使用更大的内核会给你一个更小的输出。例如,具有 3x3 内核的 4x4 输入矩阵将产生 2x2 输出矩阵,而具有 2x2 内核的 4x 4 输入矩阵将产生 3x3 输出矩阵(如果没有添加填充):
(图片由作者提供)
在转置卷积中,当内核变大时,我们将输入层的每个数字“分散”到更大的区域。因此,内核越大,输出矩阵也越大(如果不添加填充):
(图片由作者提供)
2.大步
在卷积中,步长参数表示内核在输入层上沿行和列移动的速度。如果步幅为(1,1),则内核每走一步移动一行/一列;如果步幅是(2,2),则内核每走一步移动两行/列。因此,步幅越大,到达行/列末尾的速度越快,因此输出矩阵越小(如果没有添加填充)。设置更大的步幅也可以减少相同数字的重复使用。
(图片由作者提供)
在转置卷积中,步长参数表示内核在输出层上移动的速度,如下图所示。注意,内核总是在输入层一次只移动一个数字。因此,步幅越大,输出矩阵就越大(如果没有填充)。
(图片由作者提供)
3.填料
在卷积中,我们经常想要保持输入层的形状,我们通过零填充来实现。在 Keras 中,填充参数可以是两个字符串之一:“valid”或“same”。当填充为“有效”时,意味着不实现零填充。当填充“相同”时,输入层以某种方式被填充,使得输出层具有输入形状除以步幅的形状。当跨距等于 1 时,输出形状与输入形状相同。
(图片由作者提供)
在转置卷积中,填充参数也可以是两个字符串:“valid”和“same”。但是,由于我们在转置卷积中扩展输入层,如果选择“有效”,输出形状将大于输入形状。如果使用“相同”,则输出形状被强制变成输入形状乘以步幅。如果此输出形状小于原始输出形状,则仅保留输出的中间部分。
(图片由作者提供)
记住卷积和转置卷积中的“有效”和“相同”的一种更简单的方法是:
- “有效”:不执行额外的操作。输出保持其应有的状态。
- “相同”:输出形状是输入形状除以步幅(卷积)或乘以步幅(转置卷积)。当跨距等于 1 时,输出形状总是与输入形状相同。
从头开始构建我自己的 Conv2D 和 conv 2d 转置层
到目前为止,我已经解释了所有关于转置卷积层的概念及其重要参数。对你来说,它们可能仍然非常抽象,我完全理解你,因为我也很难理解转置卷积层是如何工作的。但是不要担心,现在我们可以使用我们所学的概念来构建我们自己的卷积和转置卷积层——这一定会揭示转置卷积层的奥秘!
我们先从 Conv2D 开始:
让我们来看看我自制的 Conv2D 层:
- 首先,我定义了需要添加的零填充的数量。如果填充是“有效的”,那么我不需要添加任何填充。如果填充“相同”,我将根据以下公式计算输入层每一侧的填充数:
其中:
- o 为输出大小
- s 为步长
- m 为内核大小
- n 为输入大小
- p 为原输入层每边的填充数
此公式由计算输出形状的公式导出:
输出形状为 o = n/s.
- 然后,我通过构造一个用零填充的大矩阵来填充输入,并将原始输入放在中间。
- 之后,我使用卷积运算计算输出。在核 W 和输入 X_sub 的子集(其具有与核相同的形状)之间执行卷积运算。输出索引 i,j 的范围从 0 到最后一个适合内核的索引。例如,如果 X_padded 的形状为 4x4,而内核的形状为 3x3,那么可以装入内核的最后一个索引是 1(可以从索引 1 到索引 4 装入内核)。下面是该过程的图解说明:
使用 4x4 输入矩阵和 3X3 内核计算输出。输出中每步更新的数字以粗体显示(图片由作者提供)
我还比较了使用我的 Conv2D 和 Keras Conv2D 的结果。结果是一样的!
- “有效”填充
- “相同”填充
现在让我们建立转置卷积层:
让我们反对拆分代码:
- 输出形状首先由下面的公式定义:
如果与计算 Conv2D 输出形状的公式进行比较,您会注意到在 Conv2DTranspose 中,步长和内核大小对输出形状的影响相反。
- 为了计算输出,我使用了两对指数: i,j 沿着输入移动, i_prime,j_prime 沿着输出移动。当 i,j 变化时, i_prime,j_prime 以给定步幅的步长变化。举个例子,如果 i 从 0 变到 1,strides = (2,2),那么 i_prime 从 0 变到 2。输入矩阵中的每个值乘以内核中的所有值,结果记录在输出矩阵中。
- 然后,我定义了填充的长度。同样,如果填充是“有效的”,我不需要修改任何东西。如果填充“相同”,则输出形状必须是输入形状乘以步幅。那就是:
填充必须将原始输出形状转换为所需的输出形状:
因此,设置填充值的简单方法是:
- 最后,通过只选择与输入矩阵形状相同的中间矩阵来填充输出。
计算输出过程的图解说明如下所示:
(图片由作者提供)
现在,我们可以通过将结果与 Keras 中的 Conv2DTranspose 进行比较来验证 Conv2DTranspose 函数:
- “有效”填充:
- “相同”填充:
结果完全一样!
结论
恭喜你,你成功了!如前所述,这个笔记本是基于我个人的理解,我实际上是在 GAN 和其他所有机器学习知识上自学的。因此,如果你发现任何错误,我会很高兴知道!
用代码理解通用逼近定理
通用逼近定理声称,具有包含有限个隐藏神经元的单个隐藏层的标准多层前馈网络能够通过使用任意激活函数来逼近连续函数。(来源)
然而,神经网络逼近将输入映射到输出目标的任何连续函数的能力受到神经元数量、隐藏层和在网络训练过程中使用的许多技术的限制。直觉上,你可以认为这是关于是否可能有足够的计算单元和操作来近似一个连续的函数,该函数可以正确地将输入映射到输出。近似的能力也高度依赖于我们使用的优化程序和损失函数的效率。
建议:下载脚本,自己运行,围绕参数玩。回购是( 此处 )。如果你已经忘记了神经网络,那就来看看吧( 此处 )。
这些决定神经网络的设置和训练的参数通常被称为超参数。
我们可以在代码中调整的超参数示例:
- 网络结构。(隐藏层数,神经元数)
model = nn.Sequential(
nn.Linear(1, n_neurons),
nn.ReLU(),
#nn.Linear(n_neurons,n_neurons),
#nn.ReLU(),
nn.Linear(n_neurons,1),
nn.ReLU()
)
2.历元数(我们浏览所有数据的次数),第 57 行
3.损失函数和优化器,有这么多可用的优化器,查看一下[ 此处 ]:
optimizer = optim.RMSprop(model.parameters(), lr=learning_rate) # define optimizer
#optimizer = optim.SGD(model.parameters(), lr=learning_rate)criterion = nn.MSELoss() # define loss function
代码实验
我们可以在代码中运行一些实验来更好地理解近似的概念。假设我们试图逼近的函数具有 y=x 的关系,我们可以运行一些实验来测量单个隐藏层需要多少个神经元来拟合 y = x 曲线,并调整超参数以搜索最佳结果。
图 1:具有单一隐藏层的前馈神经网络(调整一段时间后)
从上图(图 1)中,我们可以看到在单个隐藏层中有 20 个神经元,神经网络仅通过训练输出值就能够很好地逼近函数。在单个隐藏层中增加到 50 个神经元为我们提供了更好的结果。
简要回顾一下,如果您忘记了,这是一个具有 8 个神经元的单隐层前馈网络架构的简单图示:
图 2:单隐层前馈神经网络的结构
理论上,通用逼近定理意味着当给定适当的组合值时,神经网络可以很好地逼近各种各样的函数。然而,由于在搜索这些值时训练网络时的限制/挑战,学会用适当的值构建网络并不总是可能的。
图 3:具有单一隐藏层的前馈神经网络。超参数的不良调整导致不良训练。
从上图(图 3)来看,同一个架构的单隐层,网络近似的很差。这是因为训练神经网络并不总是为我们提供精确/完美的值。因此,我们必须意识到,尽管理论上神经网络可以逼近非常精确的连续函数映射,但它可能无法逼近预期的连续函数,因为神经网络的训练过程有其自身的挑战。
图 4:具有两个隐藏层的前馈神经网络。超参数的不良调整会导致不好的训练,但结果仍然很好。
运行另一个实验,我们用 20 个神经元和 50 个神经元连接另一个隐藏层,结果可以在上图中看到(图 4)。可以观察到,预测函数的近似更好,而不需要花费太多时间来调整预期的训练参数。在寻找更好的近似时增加神经元和连接是一个很好的启发,但我们必须记住,在训练神经元的过程中也存在一些挑战,这些挑战可能会阻止神经网络学习近似函数所需的最佳值,即使理论上有足够多的节点可用。
图 5:具有单一隐藏层的前馈神经网络。超参数的出色调整带来良好的训练。
实验的另一个重要收获是,通过花更多时间调整神经网络的超参数,我们实际上可以获得一个近乎完美的近似,具有相同的 1 个隐藏层和 50 个神经元的架构,如上图所示(图 5)。可以观察到,结果甚至比使用具有不良超参数的 2 个隐藏层更好。如果我们花更多的时间调整超参数,使用两个隐藏层的实验肯定可以逼近得更好。这表明优化程序和某些超参数对训练网络是多么重要。有了 2 层和更多的神经元,不需要太多的调整就可以得到好的结果,因为有更多的连接和节点可以使用。然而,随着我们添加更多的节点和层,计算成本会变得更高。
最后,如果关系太复杂,那么 1 个具有 50 个神经元的隐藏层甚至可能在理论上不能首先足够好地近似输入到输出的映射。y=x 是一个相对容易近似的关系,但我们可以考虑图像等输入的关系。图像像素值与图像分类之间的关系极其复杂,即使是最好的数学家也不可能找到合适的函数。但是,我们可以通过添加更多的隐藏层和神经元,使用神经网络来近似这种复杂的关系。这催生了深度学习领域,它是机器学习的一个子集,专注于利用具有许多层的神经网络(例如:深度神经网络、深度卷积网络)来学习非常复杂的函数映射。
建议
请尝试其他函数,如sin(x)
或cos(x)
,看看您是否能很好地近似这个关系。您可能会一直失败,直到您得到正确的超参数,但是它会让您对优化超参数有一个很好的了解。我建议,如果函数太难近似,继续添加更多的层和神经元!尝试不同的优化器,如 SGD 和 ADAM,比较结果。
使用日志了解您的计算机系统📃
System.out.println("到此为止的作品");
作为一个程序员,你可能已经写了上面的代码段来弄清楚你的代码是怎么回事(这里是 Java)。通常,当我们试图调试代码时,我们会使用这种技巧。我们这样做是为了理解代码、执行流程并识别代码中的错误。记录和理解计算机系统执行的系统方法是系统日志。系统日志不仅仅用于调试。它们用于更具挑战性的计算机系统相关用例。其中包括系统监控、工作流建模、性能调查和异常检测。让我们看看如何使用系统日志来了解我们的计算机系统,以及当它被用作大数据时有多强大。
什么是日志数据?
计算机系统生成由系统运行时信息组成的系统日志。通常,日志文件包含一系列日志行,代表系统中发生的不同事件。系统日志是使用描述系统当前运行时信息的单独代码段写入文件的输出。以下是阿帕奇 SLF4J 登录 Java 的方式。
LOGGER.info(“Participated to a new round of number” + roundNumber + “with rank as “ + nodeRank);
下面给出了可能的原始日志行,它将通过执行上述代码段写入日志文件。
2019–11–18 20:18:29,467 [INFO] distributedConsensus.LeaderCandidate Participated to a new round of number 13 with rank as 263
一个日志文件可能包含成百上千个类似于上面例子的日志,描述你的计算机系统的故事。这些日志行可以是错误消息、警告、调试级别消息或一般信息消息或其他类型的日志消息。为了使日志记录变得简单和一致,在 Java 编程中使用了像 SLF4J 、日志回溯这样的日志记录框架。 Log4js 和 Winston 是 Node.Js 中常用的日志记录工具,这些日志记录框架为您提供了一套 API 来实现简单、一致和健壮的日志记录体验。可以将它们配置为在日志消息中嵌入附加信息,如日期、时间、详细级别和当前运行的进程(如示例所示),而无需在日志消息中明确指定。
日志文件中的原始日志行(取自 loghub 中的 Hadoop 数据集)
原木线上的鸟瞰图
生成的日志行可能包含不同的标题字段(取决于所使用的日志框架、配置和程序员希望与日志一起存储的信息)以及用自然语言编写的日志消息,该日志消息包含程序员希望记录的系统事件的详细信息。
在上面的例子中,2019–11–18 20:18:29,467 [INFO] distributedConsensus.LeaderCandidate
可以被识别为由空格分隔的标题字段的集合。这些字段被嵌入到日志行中,因为程序员已经配置了日志框架来这样做。Participated to a new round of number 13 with rank as 263
是自然语言日志消息,包含程序员想要记录的系统事件信息。这部分的内容完全取决于程序员记录的系统事件和他们使用的(英语)语言。要获得日志数据的真正优势,重要的是日志数据中提到的信息能够被人类容易地理解。
了解日志数据
日志数据可以被视为大数据的半结构化形式之一,因为原始日志行由结构化的标题和非结构化的日志消息组成。尽管我们有大量的日志数据来描述我们系统的整个故事,但是通过阅读日志来理解日志数据是非常困难的。这主要是因为日志消息部分是由人类使用(主要)英语编写的。识别日志行的标题字段并不困难,因为日志行的这一部分具有由日志框架定义的结构。在上面的例子中,我们很容易理解,2019–11–18
是日期,20:18:29,467
是时间戳,[INFO]
是详细级别,distributedConsensus.LeaderCandidate
是执行该事件的进程或组件。
理解日志数据真正困难的部分是理解自然语言日志消息,其中包含关于所记录的系统事件的最重要信息。因此,主要关注自然语言日志消息的结构化。通过观察上面生成日志行的代码段,我们可以理解程序员记录了一个与“参与一轮”相关的事件。我们还可以看到,事件可能在不同的情况下发生,因此,代码中指定的参数值(roundNumber,nodeRank)可能会因日志行的不同而不同。这是因为它们是特定事件的动态决定信息。
同一事件的一组日志消息
在日志分析研究领域中,Participated to a new round of number ..... with rank as .....
部分被识别为日志消息的恒定部分或固定部分,因为它可以在“参与一轮”类型事件的任何日志消息事件中找到。日志消息的这个常量部分完全取决于记录系统事件的程序员所使用的文字和语言。日志消息的这个常量部分的实际用途是,它是“参与一轮”类型事件的所有日志消息的一般代表。也就是说,根据日志代码语句中给变量( roundNumber 和 nodeRank 的值)的不同,同一事件的日志消息会有所不同。因此,出现在日志代码中该变量位置的值(上例中的 13 和 263、34 和 134 …)被标识为可变部分或可变部分。大多数情况下,这些可变部分由数值组成,但并非所有情况都是如此。日志消息的可变部分携带系统的动态运行时信息,因此是非常有价值的信息源。
如果我们能够将日志文件中的日志消息的常量部分与变量部分分开会怎么样?如果可能的话,我们可以生成一个结构良好、比原始日志文件更容易理解的日志报告。这种结构化日志报告可以表示为一个表,该表具有描述日志行的标识符字段的不同列和表示每个日志行实例的行。
更易于理解的结构化日志报告
这种日志报告比原始日志文件更容易理解。这就是为什么许多技术和方法被建议从日志文件生成结构化日志报告。当前日志分析系统中使用的一些技术是频繁模式挖掘方法、日志数据聚类、捕捉日志行中模式的基于启发的方法等。关于日志结构方法的更多细节将在以后的文章中描述。
快乐阅读…!
使用主成分分析(PCA)了解您的数据,并发现潜在模式
超越描述的增强型数据探索
超越均值、分布和相关性的数据探索节省时间、资源并保持健康:利用主成分分析透视变量的表面。它节省了时间和资源,因为它在一个小时的模型训练之前发现了数据问题,并且对程序员的健康有好处,因为她用更愉快的事情来换取对数据的担忧。例如,一个经过充分验证的机器学习模型可能会失败,因为一维数据的方差不足或其他相关问题。PCA 提供了有价值的见解,使您对数据属性及其隐藏的维度充满信心。
本文展示了如何利用 PCA 来理解数据集的关键属性,从而节省时间和资源,最终带来更快乐、更充实的编码生活。我希望这篇文章有助于以一致的方式应用 PCA 并理解其结果。
乔纳森·博尔巴拍摄的照片
TL;速度三角形定位法(dead reckoning)
PCA 提供了超越描述性统计的有价值的见解,并有助于发现潜在的模式。两个 PCA 度量表示 1。有多少个分量获得了方差的最大份额(解释方差),以及 2。,哪些特性与最重要的组件相关(因子加载)。这些指标交叉检查项目工作流程中的先前步骤,例如数据收集,然后可以对其进行调整。作为一个快捷易用的工具,我在本笔记本或本脚本中提供了函数do_pca()
,它对准备好的数据集进行 PCA,以在几秒钟内检查其结果。
作为安全网的数据探索
当项目结构类似于下面的结构时,准备好的数据集在 4。一步一步看描述性统计。其中最常见的是所有观察值或子组的平均值、分布和相关性。
常见项目结构
- 收集:收集、检索或加载数据
- 处理:格式化原始数据,处理缺失条目
- 工程:构建和选择特征
- 探索:检查描述符、属性
- 建模:训练、验证和测试模型
- 评估:检查结果,比较模型
当经过几个小时的工作后拥有一个干净的数据集的时刻到来时,许多人已经开始关注将模型应用于数据这一激动人心的步骤。在这个阶段,如果数据没有从天上掉下来,清理和处理,大约 80–90%的项目工作量已经完成。当然,对于建模的渴望是强烈的,但是这里有两个充分的数据探索可以节省时间的原因:
- 捕捉编码错误 →修改特征工程(步骤 3)
- 识别潜在属性 →重新思考数据收集(步骤 1)、预处理(步骤 2)或特征工程(步骤 3)
在几个小时的训练、验证和测试后,担心由于潜在的数据问题而表现不佳的模型就像一个摄影师在片场,不知道他们的模型可能会是什么样子。因此,关键信息是将数据探索视为了解您的数据的机会,了解其优势和劣势。
描述性统计常常揭示编码错误。然而,检测潜在问题可能需要更多。PCA 等分解方法有助于识别这些问题,并能够修正之前的步骤。这确保了向模型构建的平稳过渡。
用 PCA 看表面之下
无论如何,大型数据集通常需要 PCA 来降低维数。这种方法可以捕捉要素间的最大可能方差,并将观测值投影到互不相关的向量(称为分量)上。尽管如此,主成分分析除了降维还有其他用途。它还有助于发现跨特性的潜在模式。
为了专注于 Python 中的实现而不是方法论,我将跳过描述 PCA 的工作方式。有许多关于它的很棒的资源,我引用它们来代替:
- 展示常设仲裁法院运作的动画:https://setosa.io/ev/principal-component-analysis/
- PCA 在一次家庭谈话中解释道:https://stats.stackexchange.com/a/140579
- 史密斯2。主成分分析教程:此处可访问。
两个指标对于理解数据探索的 PCA 至关重要:
1。解释方差 衡量一个模型能在多大程度上反映整个数据的方差。主成分试图捕捉尽可能多的差异,这一措施表明,在多大程度上他们可以做到这一点。它有助于看到组件按解释的方差排序,第一个得分最高,所有组件的总和最多为 1。
2。因子加载 表示一个变量与一个组件的相关程度。每个组成部分都由变量的线性组合构成,其中一些变量可能比其他变量更重要。因子载荷以相关系数的形式表示,范围从-1 到 1,并使成分可以解释。
接下来的章节将 PCA 应用于来自行为领域实验的激动人心的数据,并指导使用这些指标来增强数据探索。
负荷数据:对砂砾的随机教育干预(Alan 等人,2019 年)
iris 数据集很好地充当了几种 PCA 的典型例子。为了多样化并使用来自实地研究的新数据,我依赖于 Alan 等人[1]的复制数据。我希望这是值得赞赏的。
它包括来自土耳其学校行为实验的数据,10 岁的孩子参加了一个课程,以提高一种叫做毅力的非认知技能,这种技能被定义为坚持不懈地追求一项任务。作者对个体特征进行了采样,并进行了行为实验,以测量接受项目(grit == 1
)和参加控制治疗(grit == 0
)的人之间的潜在治疗效果。
下面的代码从一个 URL 加载数据,并将其存储为 pandas dataframe。
# To load data from Harvard Dataverse
import io
import requests# load exciting data from URL (at least something else than Iris)
url = ‘[https://dataverse.harvard.edu/api/access/datafile/3352340?gbrecs=false'](https://dataverse.harvard.edu/api/access/datafile/3352340?gbrecs=false')
s = requests.get(url).content# store as dataframe
df_raw = pd.read_csv(io.StringIO(s.decode(‘utf-8’)), sep=’\t’)
预处理和特征工程
为了使 PCA 发挥作用,数据必须是数字的、无遗漏的和标准化的。我将所有步骤放入一个函数(clean_data
)中,该函数返回一个具有标准化特征的数据帧。并执行项目工作流程的步骤 1 至 3(收集、处理和工程)。首先,导入必要的模块和包。
import pandas as pd
import numpy as np# sklearn module
from sklearn.decomposition import PCA# plots
import matplotlib.pyplot as plt
import seaborn as sns
# seaborn settings
sns.set_style("whitegrid")
sns.set_context("talk")# imports for function
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
接下来,定义clean_data()
函数。它提供了一种将原始数据转换为准备好的数据集的快捷方式,该数据集具有(I)选定的要素,(ii)漏项由列表示代替,以及(iii。)标准化变量。
关于所选功能的说明:我在(iv)中选择了功能。)根据他们的复制脚本,可在 Harvard Dataverse 上访问,并且仅使用样品 2(公开访问的工作文件中的“样品 B”)。为简明起见,请参考论文中的相关描述(第 30 页,表 2)。
准备数据需要一行代码(v)。
def clean_data(data, select_X=None, impute=False, std=False):
"""Returns dataframe with selected, imputed
and standardized features
Input
data: dataframe
select_X: list of feature names to be selected (string)
impute: If True impute np.nan with mean
std: If True standardize data
Return
dataframe: data with selected, imputed
and standardized features
"""
# (i.) select features
if select_X is not None:
data = data.filter(select_X, axis='columns')
print("\t>>> Selected features: {}".format(select_X))
else:
# store column names
select_X = list(data.columns)
# (ii.) impute with mean
if impute:
imp = SimpleImputer()
data = imp.fit_transform(data)
print("\t>>> Imputed missings")
# (iii.) standardize
if std:
std_scaler = StandardScaler()
data = std_scaler.fit_transform(data)
print("\t>>> Standardized data")
return pd.DataFrame(data, columns=select_X)# (iv.) select relevant features in line with Alan et al. (2019)
selected_features = ['grit', 'male', 'task_ability', 'raven', 'grit_survey1', 'belief_survey1', 'mathscore1', 'verbalscore1', 'risk', 'inconsistent']# (v.) select features, impute missings and standardize
X_std = clean_data(df_raw, selected_features, impute=True, std=True)
现在,数据已准备好供探索。
Scree 图和因子载荷:解释 PCA 结果
PCA 产生与数据探索相关的两个度量:首先,每个分量解释了多少方差(scree plot),其次,变量与分量有多少相关性(因子加载)。以下部分提供了一个实际示例,并通过 PCA 输出提供了一个解释方差的 scree 图和一个因子负荷热图。
解释方差显示变量的维数
如今,数据非常丰富,数据集的规模也在持续增长。数据科学家通常要处理数百个变量。然而,这些变量值得他们记忆吗?换句话说:一个变量捕捉独特的模式还是测量其他变量已经反映的相似属性?
主成分分析可以通过每个成分的解释方差来回答这个问题。它详细说明了观察到最大差异的基础维度的数量。
下面的代码从 sklearn 初始化一个 PCA 对象,并沿着计算出的分量(I .)转换原始数据。此后,检索关于解释的差异的信息(ii。)和印刷(iii。).
# (i.) initialize and compute pca
pca = PCA()
X_pca = pca.fit_transform(X_std)# (ii.) get basic info
n_components = len(pca.explained_variance_ratio_)
explained_variance = pca.explained_variance_ratio_
cum_explained_variance = np.cumsum(explained_variance)
idx = np.arange(n_components)+1df_explained_variance = pd.DataFrame([explained_variance, cum_explained_variance],
index=['explained variance', 'cumulative'],
columns=idx).Tmean_explained_variance = df_explained_variance.iloc[:,0].mean() # calculate mean explained variance# (iii.) Print explained variance as plain text
print('PCA Overview')
print('='*40)
print("Total: {} components".format(n_components))
print('-'*40)
print('Mean explained variance:', round(mean_explained_variance,3))
print('-'*40)
print(df_explained_variance.head(20))
print('-'*40)PCA Overview
========================================
Total: 10 components
----------------------------------------
Mean explained variance: 0.1
----------------------------------------
explained variance cumulative
1 0.265261 0.265261
2 0.122700 0.387962
3 0.113990 0.501951
4 0.099139 0.601090
5 0.094357 0.695447
6 0.083412 0.778859
7 0.063117 0.841976
8 0.056386 0.898362
9 0.052588 0.950950
10 0.049050 1.000000
----------------------------------------
解释:第一个成分约占解释方差的 27%。与其他数据集相比,这相对较低,但没关系。它只是表明大部分观察值(100%–27% = 73%)分布在不止一个维度上。接近输出的另一种方法是问:需要多少组件来覆盖超过 X%的方差?例如,我想降低数据的维数,并保留原始数据至少 90%的方差。那么我将不得不包括 9 个组成部分,以达到至少 90%,甚至在这种情况下有 95%的解释方差。在原始数据集中总共有 10 个变量的情况下,降低维度的范围是有限的。此外,这表明 10 个原始变量中的每一个都增加了一些独特的模式,并有限地重复了来自其他变量的信息。
再举一个例子,我列出了“the”葡萄酒数据集的解释方差:
PCA Overview: Wine dataset
========================================
Total: 13 components
----------------------------------------
Mean explained variance: 0.077
----------------------------------------
explained variance cumulative
1 0.361988 0.361988
2 0.192075 0.554063
3 0.111236 0.665300
4 0.070690 0.735990
5 0.065633 0.801623
6 0.049358 0.850981
7 0.042387 0.893368
8 0.026807 0.920175
9 0.022222 0.942397
10 0.019300 0.961697
11 0.017368 0.979066
12 0.012982 0.992048
13 0.007952 1.000000
----------------------------------------
这里,13 个成分中的 8 个足以捕获至少 90%的原始方差。因此,降维的空间更大。此外,它表明一些变量对数据中的方差贡献不大。
一个 scree plot 代替了纯文本,可视化了跨组件的解释方差,并告知每个组件的单独和累积解释方差。下一个代码块创建了这样一个 scree plot,并包括一个选项,当处理大型数据集的数百个组件时,可以将注意力集中在前 X 个组件上。
#limit plot to x PC
limit = int(input("Limit scree plot to nth component (0 for all) > "))
if limit > 0:
limit_df = limit
else:
limit_df = n_componentsdf_explained_variance_limited = df_explained_variance.iloc[:limit_df,:]#make scree plot
fig, ax1 = plt.subplots(figsize=(15,6))ax1.set_title('Explained variance across principal components', fontsize=14)
ax1.set_xlabel('Principal component', fontsize=12)
ax1.set_ylabel('Explained variance', fontsize=12)ax2 = sns.barplot(x=idx[:limit_df], y='explained variance', data=df_explained_variance_limited, palette='summer')
ax2 = ax1.twinx()
ax2.grid(False)ax2.set_ylabel('Cumulative', fontsize=14)
ax2 = sns.lineplot(x=idx[:limit_df]-1, y='cumulative', data=df_explained_variance_limited, color='#fc8d59')ax1.axhline(mean_explained_variance, ls='--', color='#fc8d59') #plot mean
ax1.text(-.8, mean_explained_variance+(mean_explained_variance*.05), "average", color='#fc8d59', fontsize=14) #label y axismax_y1 = max(df_explained_variance_limited.iloc[:,0])
max_y2 = max(df_explained_variance_limited.iloc[:,1])
ax1.set(ylim=(0, max_y1+max_y1*.1))
ax2.set(ylim=(0, max_y2+max_y2*.1))plt.show()
scree 图可能显示从一个组件到另一个组件的明显跳跃。例如,当第一个组成部分比其他部分不成比例地获得更多的方差时,这可能是一个迹象,表明变量提供了相同的潜在因素,或者没有增加额外的维度,但从稍微不同的角度说了相同的事情。
为了给出一个直接的例子,并感受一下跳跃看起来有多明显,我提供了波士顿房价数据集的 scree 图:
PCA 节省时间的两个原因
假设你有数百个变量,应用主成分分析,你会发现前几个成分已经包含了大部分解释的方差。这可能暗示底层维度的数量比变量的数量少得多。最有可能的是,减少几百个变量导致培训、验证和测试的性能提升。与等待模型本身发现几个变量背后缺乏方差相比,选择一个合适的模型并对其进行改进会有更多的时间。
除此之外,想象数据是由自己构建的,例如通过 web 抓取,并且抓取器从网页中提取预先指定的信息。在这种情况下,检索到的信息可能是一维的,此时 scraper 的开发人员脑子里只有几个相关的项目,但却忘记了包括揭示问题设置的其他方面的项目。在这个阶段,回到工作流程的第一步并调整数据收集可能是值得的。
发现特性和组件之间相互关联的潜在因素
除了解释方差之外,PCA 还提供了另一个有价值的统计量:每个主成分和一个变量之间的相关性,也称为因子负载。这种统计有助于掌握组件背后的维度。例如,数据集包括关于个人的信息,如数学成绩、反应时间和保持时间。最重要的维度是认知技能,与这些变量密切相关的部分可以被解释为认知技能维度。同样,当数据具有自信、耐心或责任心等特征时,另一个维度可能是非认知技能和个性。捕捉该区域的组件与那些特征高度相关。
以下代码创建了一个热图来检查这些相关性,也称为因素加载矩阵。
# adjust y-axis size dynamically
size_yaxis = round(X_std.shape[1] * 0.5)
fig, ax = plt.subplots(figsize=(8,size_yaxis))# plot the first top_pc components
top_pc = 3
sns.heatmap(df_c.iloc[:,:top_pc], annot=True, cmap="YlGnBu", ax=ax)
plt.show()
第一个因素与任务能力、推理分数( raven )、数学分数、语言分数强烈负相关,并与坚韧不拔的信念( grit_survey1 )积极相关。将此归纳为一个共同的潜在因素是主观的,需要领域知识。在我看来,第一个组成部分主要是捕捉认知技能。
第二个因素与接受治疗(勇气)、性别(男性)负相关,与不一致正相关。解释这个维度不那么清晰,也更具挑战性。然而,它解释了 12%的变异,而不是像第一个组成部分那样解释了 27%,这导致了更难解释的维度,因为它稍微跨越了几个主题领域。接下来的所有组件可能同样难以解释。
变量获取相似维度的证据可能是均匀分布的因子载荷。启发我写这篇文章的一个例子是关于我的项目的,在那里我依靠谷歌趋势数据和关于公司可持续性的自建关键词。第一个主成分的第 15 个最高因子载荷列表显示载荷范围从最高值 0.12 到最低值 0.11。因子载荷的这种均匀分布可能是一个问题。当数据是自己收集的,并且有人预先选择了要收集的内容时,这一点尤其适用。调整此选择可能会增加数据的维数,最终可能会提高模型性能。
PCA 节省时间的另一个原因是
如果数据是自行构建的,则因子加载显示每个特征如何影响基础维度,这有助于提出关于数据收集的 额外观点 以及哪些特征或维度可以增加有价值的差异。不是盲目猜测要添加哪些功能,因素加载导致 明智的数据收集决策 。它们甚至可能是寻找更高级功能的灵感。
结论
总之,PCA 是数据探索工具箱中的一个灵活工具。其主要目的是降低大型数据集的复杂性。但它也有助于观察变量的表面,发现潜在的维度,并将变量与这些维度联系起来,使它们变得可以解释。要考虑的关键指标是解释方差和因子加载。
本文展示了如何利用这些指标进行超出平均值、分布和相关性的数据探索,并建立对数据底层属性的理解。识别跨变量的模式对于重新思考项目工作流程中的先前步骤是有价值的,例如数据收集、处理或特征工程。
感谢阅读!我希望你会发现它和我写这个指南一样有用。我很想知道你对这件事的想法。如果您有任何反馈,我非常感谢您的反馈,并期待收到您的消息。
附录
访问 Jupyter 笔记本
我使用do_pca()
将主成分分析应用于更典型的数据集,如波士顿房地产市场、葡萄酒和虹膜。它展示了小数据集的 PCA 输出。随意下载我的笔记本或者剧本。
关于因子分析与主成分分析的说明
这里有一个经验法则陈述:如果你想把你的相关观察变量减少到一个更小的不相关变量集,使用主成分分析来测试观察变量的潜在因素模型。
尽管这种区分在科学上是正确的,但在实际应用中却变得不那么相关了。PCA 与因子分析密切相关,因子分析通常会得出与我们所关心的数据属性相似的结论。因此,对于数据探索,区别可以放宽。本帖给出了一个应用环境中的示例,另一个带有因子分析实际操作代码的示例附在笔记本中。
最后,对于那些对因子分析和 PCA 之间的差异感兴趣的人,请参考这篇文章。请注意,在整篇文章中,为了准确起见,我从未使用过术语潜在因素。
参考
[1]艾伦,s .,博内瓦,t .,&厄塔克,S. (2019)。曾经失败,再试一次,更成功:一项关于毅力的随机教育干预的结果。经济学季刊,134(3),1121–1162。
2史密斯,L. I. (2002 年)。主成分分析教程。
理解 zip()—Python 中隐藏的瑰宝
有效地合并未知数量的列表
由 Fabrizio Verrecchia 在 Unsplash 上拍摄的照片
在 Python 中,有几种方法可以合并 2 个列表/元组/集/字典。我所说的“合并”不仅仅是将一个元素附加到另一个元素上,而是将具有相同索引的元素分组。如果你在面试中,面试官要求你实现以下逻辑,你的第一个“蛮力”回答会是什么?
作者:高
大概你会想出这样的东西。程序遍历city
的长度,每次都从具有相同索引的country
和city
中获取值,然后将它们放入一个元组中。这绝对是一个可行的解决方案,但不是你的面试官所期待的。
稍微改进一下,可以用enumerate
迭代city
。enumerate
返回列表中每个元素的索引和值。所以我们可以用ci
代替city[i]
。但这仍然不是理想的答案。
zip()内置函数
Python 有许多隐藏的宝石,而zip()
就是其中之一。它能够以更干净的方式解决同样的问题。我们都喜欢干净的代码,不是吗?
根据官方 Python 文档,
zip(*iterables)
创建了一个迭代器,它聚集了来自每个迭代器的元素。
向拆包操作员重述(*)
单个星号(*)意味着它解包迭代器。例如,如果您有两个列表list1=["a1","a2"]
和list2=["b1","b2"]
,您不必创建一个新的参数列表并将其传递给类似zip([list1,list2])
的函数,相反,您可以只做zip(list1, list2)
。
当您不知道函数的参数个数时,可以使用解包运算符(*)。比如你可以计算一个未知数量的自变量之和。
另一个拆包操作符是(**)。两个星号代表字典。这意味着每个参数必须有一个键,这就是为什么你通常把**kwargs
(关键字参数)作为输入名。明确一下,你可以给它取任何名字,比如**nums
。
如果你想了解更多关于*args
和**kwargs
的知识,我推荐你阅读 Python args 和 kwargs:来自真实 Python 的去神秘化 。
[## Python 参数和 kwargs:去神秘化——真正的 Python
在这个循序渐进的教程中,您将学习如何在 Python 中使用 args 和 kwargs 来为您的…
realpython.com](https://realpython.com/python-kwargs-and-args/)
**zip()**
vs**zip_longest()**
再来说说zip()
。我们知道zip(*iterables)
的输入是一些迭代器。迭代器可以是一个字符串、列表、元组、集合或字典。在内部,zip()
对所有迭代器进行多轮循环。在每一轮中,它对每个迭代器调用next()
函数,并将值放入一个元组中,在这一轮结束时产生元组。然后继续下一轮。如果任何迭代器用尽,循环将结束。也许看代码更容易。Python 文档给出了下面的代码来帮助读者理解zip()
是如何工作的。
zip_implementation.py (来源: python doc )
那我们用zip()
来解决前面的问题吧。每次都会产生一个元组。使用 yield 的一个优点是节省 RAM 的使用,因为我们不需要将整个结果序列存储在内存中。
默认的zip()
遵循坎尼金定律,这意味着合并结果的长度取决于最短的输入迭代。这就是为什么在前面的例子中,您在输出中看不到国家X
。
康尼金定律(来源:博客)
但是,如果希望合并后的结果与最长的输入迭代器对齐,可以使用[itertools.zip_longest](https://docs.python.org/3.3/library/itertools.html#itertools.zip_longest)
。在这种情况下,缺少的值将由用户定义的fillvalue
填充。
拉开
既然我们可以“压缩”2 个迭代器,我们也必须能够“解压缩”它。逻辑是unzipped = zip(*zip(*iterables))
。在示例代码中,zipped
和unzipped
对象都有类zip
,它们是不可读的。我们可以将它们转换成一个列表或一个元组。
边缘案例
对输入迭代器的数量没有限制。它可以是 0、1 或更多。拥有 0 个或 1 个输入迭代器的情况并不常见,但这仍然是可能的。
字典中的 **zip()**
以下代码的结果会是什么?在进一步阅读之前思考一下。
答案是("city", "city", "city")
和(“country”, “country”, “country”)
。为什么我们只有钥匙?
根据我们之前看到的,在内部zip()
对每个输入参数执行iter()
。所以问题是iter(nl)
的产量是多少?如果我们执行print(iter(nl))
,我们将得到<dict_keyiterator object at 0x10e1e3f50>
。这就是为什么我们在结果中只接收键。
更好的方法是使用nl.items()
作为输入。在这种情况下,我们能够在循环中接收键和值。
中的混合型**zip()**
迭代器的类型也没有限制。在下面的代码中,我们使用混合类型作为输入参数。结果会怎样?想一想。这个例子只是出于教育目的。它不应该是你见过的最好的代码。🙂
好吧,答案是:
('w', 'Amsterdam', ('key1', 'Netherlands'))
('o', 'Berlin', ('key2', 'Germany'))
('r', 'Chongqing', ('key3', 'China'))
每个ele
是来自 3 个输入参数的 3 个元素的元组。string
的长度应该考虑为 5,而不是 1。每个循环将返回 1 个字符。程序总共循环 3 次,因为输入的最短长度是 3。
如果你能回答这个问题并解释背后的原因,那么你已经掌握了这一章。
结论
zip()
给了我们用干净的代码合并未知数量的迭代器的便利。当我们有许多大小相同的迭代器时,效果最好。zip()
创建一个生成器,因此它能够处理长输入并节省 RAM 的使用。
同时,我们应该注意zip()
的一些行为:
zip()
遵循康尼金定律。它不断产生元组,直到所有迭代器都用完为止。如果迭代器的长度不匹配,它不会抛出异常。itertools.zip_longest
通过用用户自定义的fillvalue
填充缺失值,解决了“长度”问题。- 使用
zip()
中的str
和dict
时要小心。你需要很好地理解iter
如何与str
和dict
一起工作。
我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
参考
[## 2.内置函数- Python 3.3.7 文档
Python 解释器内置了许多始终可用的函数和类型。它们被列出…
docs.python.org](https://docs.python.org/3.3/library/functions.html#zip) [## Python zip 示例|用于迭代的 Python zip()函数
Python zip()是一个返回 zip 对象的内置函数,该对象是元组的迭代器,其中第一项…
appdividend.com](https://appdividend.com/2019/04/09/python-zip-example-python-zip-function-tutorial/)
了解 8 种交叉验证
交叉验证及其类型的深入解释
交叉验证也被称为抽样外技术是数据科学项目的一个基本要素。这是一个重采样过程,用于评估机器学习模型,并评估该模型在独立测试数据集上的表现。
在这篇文章中,你可以读到 8 种不同的交叉验证技术,它们各有利弊,如下所列:
- 留 p 不交叉验证
- 漏项交叉验证
- 维持交叉验证
- 重复随机子采样验证
- k 倍交叉验证
- 分层 k 倍交叉验证
- 时间序列交叉验证
- 嵌套交叉验证
在讨论交叉验证技术之前,让我们了解一下为什么要在数据科学项目中使用交叉验证。
为什么交叉验证很重要?
我们经常将数据集随机分为训练数据和测试数据,以开发机器学习模型。训练数据用于训练 ML 模型,并且在独立的测试数据上测试相同的模型,以评估模型的性能。
随着分裂的随机状态的改变,模型的精度也改变,因此我们不能实现模型的固定精度。测试数据应该独立于训练数据,这样就不会发生数据泄漏。在使用训练数据开发 ML 模型期间,需要评估模型性能。这就是交叉验证数据的重要性。
数据需要分成:
- 训练数据:用于模型开发
- 验证数据:用于验证同一型号的性能
(图片由作者提供),验证分割
简单来说,交叉验证使我们能够更好地利用我们的数据。你可以进一步阅读、工作和实施 7 种类型的交叉验证技术。
1.留下 p-out 交叉验证:
保留 p-out 交叉验证(LpOCV)是一种详尽的交叉验证技术,它使用 p-观察值作为验证数据,其余数据用于训练模型。以各种方式重复这一过程,以在一组 p 观察值和一组训练集上切割原始样本。
p=2 的 LpOCV 的一种变体称为留对交叉验证,已被推荐为一种近乎无偏的方法,用于估计二元分类器的 ROC 曲线下的面积。
2.留一交叉验证:
留一法交叉验证(LOOCV)是一种详尽的交叉验证技术。这是一个 p=1 的 LpOCV 范畴。
(来源),LOOCV 运营部
对于具有 n 行的数据集,选择第一行进行验证,其余(n-1)行用于训练模型。对于下一次迭代,选择第 2 行进行验证,其余的用于训练模型。类似地,重复该过程,直到 n 个步骤或期望的操作次数。
以上两种交叉验证技术都是穷举交叉验证的类型。穷举交叉验证方法是以所有可能的方式学习和测试的交叉验证方法。它们具有相同的优点和缺点,讨论如下:
优点:
- 简单、易于理解和实施。
缺点:
- 该模型可能导致较低的偏差。
- 所需的计算时间很长。
3.维持交叉验证:
维持技术是一种彻底的交叉验证方法,它根据数据分析将数据集随机拆分为定型数据和测试数据。
(图片由作者提供),70:30 将数据分别拆分为训练数据和验证数据
在维持交叉验证的情况下,数据集被随机分为定型数据和验证数据。一般来说,训练数据的分裂多于测试数据。训练数据用于归纳模型,验证数据用于评估模型的性能。
用于训练模型的数据越多,模型就越好。对于维持交叉验证方法,大量数据是从训练中分离出来的。
优点:
- 和以前一样。
缺点:
- 不适合不平衡的数据集。
- 许多数据是从训练模型中分离出来的。
4.k 倍交叉验证:
在 k 折叠交叉验证中,原始数据集被均等地划分成 k 个子部分或折叠。对于每次迭代,从 k 个折叠或组中选择一个组作为验证数据,并且选择剩余的(k-1)个组作为训练数据。
(来源),k 倍交叉验证
该过程重复 k 次,直到每组被视为有效,并作为训练数据保留。
(图片由作者提供),k 倍交叉验证
通过取 k-模型验证数据的平均精度来计算模型的最终精度。
LOOCV 是 k 倍交叉验证的变体,其中 k=n
优点:
- 该模型具有较低的偏差
- 低时间复杂度
- 整个数据集用于训练和验证。
缺点:
- 不适合不平衡的数据集。
5.重复随机子采样验证:
重复随机子采样验证也称为蒙特卡罗交叉验证,它将数据集随机分为训练和验证。不太可能将数据集 k 倍交叉验证拆分为非分组或折叠,但在这种情况下是随机拆分。
迭代的次数不是固定的,而是由分析决定的。然后对分割的结果进行平均。
(图片由作者提供),重复随机子采样验证
优点:
- 训练和验证分割的比例不依赖于迭代或分区的数量。
缺点:
- 一些样本可能不会被选择用于训练或验证。
- 不适合不平衡的数据集。
6.分层 k 倍交叉验证:
对于上面讨论的所有交叉验证技术,它们可能不适用于不平衡的数据集。分层 k-fold 交叉验证解决了不平衡数据集的问题。
在分层 k 折叠交叉验证中,数据集被划分成 k 个组或折叠,使得验证数据具有相同数量的目标类标签实例。这确保了一个特定的类不会过度出现在验证或训练数据中,尤其是当数据集不平衡时。
(图片由作者提供),分层 k-fold 交叉验证,每个 fold 都有相同的目标类实例
通过取每个折叠得分的平均值来计算最终得分。
优点:
- 适用于不平衡的数据集。
缺点:
- 现在适用于时间序列数据集。
7.时间序列交叉验证:
数据的顺序对于时间序列相关问题非常重要。对于时间相关数据集,将数据随机分割或 k 倍分割成训练和验证可能不会产生好的结果。
对于时间序列数据集,根据时间将数据拆分为训练和验证,也称为正向链接法或滚动交叉验证。对于特定的迭代,训练数据的下一个实例可以被视为验证数据。
(图片由作者提供),时间序列交叉验证
如上图所示,对于第一次迭代,前 3 行被视为训练数据,下一个实例 T4 是验证数据。训练和验证数据选择机会被转发用于进一步的迭代。
8.嵌套交叉验证:
在 k-fold 和分层 k-fold 交叉验证的情况下,我们在训练和测试数据中得到一个差的误差估计。在早期的方法中,超参数调整是单独进行的。当交叉验证同时用于调整超参数和推广误差估计时,需要嵌套交叉验证。
嵌套交叉验证可适用于 k 倍和分层 k 倍变异。阅读下面的文章,了解更多关于嵌套交叉验证及其实现的信息:
[## 使用 Python 进行机器学习的嵌套交叉验证——机器学习掌握
k-fold 交叉验证程序用于评估机器学习模型的性能。
machinelearningmastery.com](https://machinelearningmastery.com/nested-cross-validation-for-machine-learning-with-python/)
结论:
交叉验证用于比较和评估 ML 模型的性能。在本文中,我们讨论了 8 种交叉验证技术及其优缺点。k-fold 和分层 k-fold 交叉验证是最常用的技术。时间序列交叉验证最适用于与时间序列相关的问题。
这些交叉验证的实现可以在 sklearn 包中找到。阅读该 sklearn 文档了解更多详情。
参考资料:
[1]维基百科:https://en . Wikipedia . org/wiki/Cross-validation _(statistics)
感谢您的阅读
通过 R 中的划痕编码的神经网络;新手向导
动手香草建模第二部分
作者图片
如果你曾经深入研究过数据科学的世界,那么我认为你现在肯定在探索机器学习和人工智能或一般数据科学的旅程中的某个时候,在某个地方遇到过术语 【神经网络 ,这并不荒谬。
图一。只有一个隐藏层的神经网络(图片由作者提供)
最被认可的神经网络(NN)定义是它是一种受大脑启发的计算机架构,包含各种功能的网络拓扑,其中节点以特定的方式相互连接,通过迭代地遵循一系列算法来揭示数据集中的潜在模式(图 1)。也许,只要有一点数学上的成熟和一点最优化理论的知识,把神经网络称为函数逼近器或回归器就简单得犯罪了。从我以前的文章中,我们通过一个实际的例子看到了 Keras 和 Tensorflow 等高级 API 如何使构建和训练神经网络变得非常简单。然而,Keras 和 Tesnsorflow 所提供的简单性困扰着一个具有顽强依赖性的初学者,并使新手真正难以理解 学习 (前馈-反向传播)过程背后的实际底层数学和推理。从我的文章中,你可能会发现我是一个非常视觉化的人,当谈到学习时,我认为引人入胜的视觉效果有助于更深层次的理解。在这里,我将尝试稍微揭开这个黑匣子,我们将温和地深入到包含梯度下降优化器的神经网络的数学形式中,并全面尝试构建我们自己的网络。如果你对初等线性代数和微分方程不太熟悉,不要担心,因为视觉效果足以让这个概念在你的大脑控制台中滚动。同样,如果你对这些数学前提感到满意,那么你一定会意识到这个模型是多么的夸张。
现在,我们开始吧!!
神经网络 架构
带有一个隐藏层的简单神经网络架构如图 2 所示。第一层叫做 输入层 。数据集的每个特征充当单个 神经元/节点 (红色)。给定一组 权重 ,这些节点的线性组合连同(蓝色)生成下一个顺向层的单个节点,称为 隐藏层 (黄色)。注意,输入层的 iᵗʰ 节点在形成隐藏层值 hⱼ 的 jᵗʰ 节点中的贡献是 iᵗʰ节点值 Xᵢ 和 jᵗʰ权重元组即 wⱼᵢ.的 iᵗʰ元素的乘积然后对隐藏层和输出层重复类似的方案。
图二。阐释神经网络(图片由作者提供)
在这一点上,我建议您尝试将权重元组可视化为列向量,组合形成一个矩阵 W 行等于当前层中的节点数,列等于后续层中的节点数。因此,对于动画中的给定网络,对应于输入层和隐藏层的权重矩阵 W 将为:
这种对权重的矩阵理解对于理解即将到来的基础数学很重要。
因为我们都知道神经网络的概念化是受大脑功能的启发,在这一点上,提到脑细胞/神经元的突触末端及其与神经网络的类比是很重要的。由上述操作产生的每个神经元值都类似于脑细胞携带的刺激,并且应该将其传递给下一个连接的神经元。轴突的突触末端有一种机制,决定刺激是否足够强大,可以传递到下一个神经元的树突。类似地,该激活由 NN 中的激活功能执行。因此,准确地说,每个生成的节点值都通过一个激活函数传递,该函数决定是否传递激励以及传递多少激励。
图 3。范围从-inf 到+inf 的 sigmoid 函数(图片由作者提供)
最常用的激活函数有 Sigmoid 、 ReLU、Tanh、和 Softmax (广义的 Sigmoid)。虽然 ReLU 函数只允许传递正值,但是 sigmoid 激活函数, S(x) 唯一地将任意实数 x 映射到范围(0,1)中图 3。数学上,s(x)=1/(1+eˣ)。有趣的是,的微分即 dS(x)/dx 等于 S(x)(1-S(x)) 。这在计算梯度时带来了极大的数学便利,也是为什么 sigmoid 是最著名的激活函数的原因。这个从一层到另一层的信息转换过程叫做前馈。**
一旦估计了最终输出层,则使用误差指数函数 P 来评估性能。数学上,P =lld—zll/2,** 其中 d 是实际/期望输出, z 是估计输出。优化目标是最小化 P. 实践中有许多优化器,如牛顿下降法、ADAM 等。这里我们将重点介绍最流行、应用最广泛的梯度下降法。**
既然 P 是 z 的函数,而后者又是权重Wb .所以要尽量减少 P、 权重、的变化******
图 4(作者图片)
现在让我们看看如何计算这些所需的梯度。因此,如果我们用图 2 中的实例映射这个前奏,这个过程看起来就像下图(图 5)。注意,为了计算性能指数相对于权重的梯度,即 dP/dW ₁和 dP/dW ₂,我们从过程的右端开始应用链式法则,向后传播。(回想一下,性能指标是P =lld—zll/2。** )。这个过程被称为**反向传播。****
图 5。反向传播中的链式法则(图片由作者提供)
梯度▽p =σdpᵢ/dwᵢ对于 i = 1,2,3…,k+1 其中 k 为建筑深度。一旦 ▽ P 被评估,权重和偏差分别升级为:
W _ new = W _ current—【η(【P】b _ new = b _ current—【η(【P)
其中值 η 为学习率。这两个方程非常重要,我们会经常用到它们。有了这些笔记,我们现在将一步一步地制作我们的模型
逐步建模
为了建模一个简单的基于神经网络的分类模型,我将使用 Iris 数据集,该数据集具有四个特征和一个包含三个不同类别的标签向量。我们的模型将包含两个大小为 4 和 3 神经元的隐藏层。该模型如图 6 所示。
图 6。我们的模型(图片由作者提供)
一个热编码
一个热编码,这是类编码的标签被移除并且为每个唯一标签值添加新的二进制变量{0,1}的地方。
*set.seed(123)
data<-as.matrix(iris) #iris datasetclasses<-unique(data[,5])# retreive unique classes###########ONE HOT ENCODING###########
#Initializing an empty matrix
hot_encode<-matrix(0, ncol=length(unique(classes)), byrow = T,
nrow= nrow(data),
dimnames = list(NULL, c(unique(classes))))#Imputing 1 at respective classfor(i in 1:nrow(data)){
for(j in 1:3){
if(data[i,5] == classes[j]){hot_encode[i,j]<-1}
else next
}
}# Combining the data and encoded labels
data<-as.matrix(cbind(iris[,1:4], hot_encode))
data<-data[sample(1:nrow(data)), ]set.seed(123)
seq<-sample(1:nrow(data)) # preserving the shuffle orderhead(data)*
定义功能
我们将需要一个 Sigmoid 激活函数【S(x)】及其导数函数【S(x)【1-S(x)】。此外,我们需要规范化的输入数据,即我们的虹膜数据集的特征列。
*############ Sigmoid Activation Function######
activation<-function(x){
y<- 1/(1+exp(-x)) #Sigmaod function
return(y)
}###########Derivative of Sigmoid Function#####
derivative_activation<-function(x){
y<-x*(1-x) #derivative of sigmoid function
return(y)
}###########Normalization Function############
normalize<-function(x){
for(i in 1:ncol(x)){
x[,i]<-(max(x[,i])-x[,i])/(max(x[,i])-min(x[,i]))
}
return(x)
}#Normalizing the input before feeding to an NN is a good practice###
data[, 1:4]<-normalize(data[, 1:4])*
定义模型的结构并初始化权重矩阵
*neuron_input<- 4 #Define number of neurons in Input layer
neuron_layer1<- 4 #Define number of neurons in first hidden layer
neuron_layer2<- 3 #Define number of neurons in second hidden layer
neuron_output<- 3 #Define number of neurons in the output layer#initalizing weight W1 and bias b1
set_weight_1<-matrix(runif(4*4, 0,1),
ncol= neuron_layer1, nrow= neuron_input)
bias_1<-runif(neuron_layer1, 0,1)#initalizing weight W2 and bias b2
set_weight_2<-matrix(runif(4*3, 0,1),
ncol= neuron_layer2, nrow= neuron_layer1)
bias_2<-runif(neuron_layer2, 0,1)#initalizing weight W3 and bias b3
set_weight_3<-matrix(runif(3*3, 0,1),
ncol= neuron_output, nrow= neuron_layer2)
bias_3<-runif(neuron_output, 0,1)################# TRAINING SET #################
input_layer<-data[1:120, 1:4]
label<-data[1:120, 5:7]################# TEST SET #####################
test<-data[121:150, 1:4]
test_label<-as.integer(iris$Species[seq[121:150]])#--------------------------------------------------------#
lr=0.1 # Learning Rate
er<-NA # The performance function value
itr<-1 # Iteration/epoch
accuracy<-NA #Training Accuracy
t.accuracy<-NA #Test Accuracy
loss<-NA #loss vector containing the error value at current epoch*
模型拟合
从这里开始,逐步实现模型拟合。参考图 7,它通过仅显示前 10 个实例来说明梯度是如何计算的。
*while(itr <= 5000){
print(paste("epoch =", itr)) #print the current epoch
itr<-itr+1 #Update the iteration number
###############FORWARD FEED##################################
#-----------------------STEP 1-----------------------------#
hidden_layer_1<-t(t(input_layer %*% set_weight_1) + bias_1)
activated_hidden_layer_1<-activation(hidden_layer_1)
#-----------------------STEP 2-----------------------------#
hidden_layer_2<-t(t(activated_hidden_layer_1 %*% set_weight_2) + bias_2)
activated_hidden_layer_2<-activation(hidden_layer_2)
#-----------------------STEP3------------------------------#
final_layer<-activation(t(t(activated_hidden_layer_2 %*% set_weight_3) + bias_3))
#-----------------------STEP4------------------------------#
er<-sum(((label-final_layer)^2)/2)/120
error<- -(label-final_layer)
loss[itr]<-er
###################BACKPROPOGATION#################################
#-------------------------STEP5-----------------------------#
derivation_final_layer<-derivative_activation(final_layer)
delta_final_layer<- derivation_final_layer * error
#-------------------------STEP6-----------------------------#
derivative_hidden_layer_2<-derivative_activation(activated_hidden_layer_2)
error_layer_2<-delta_final_layer%*%t(set_weight_3)
delta_layer_2<- derivative_hidden_layer_2 * error_layer_2
#-------------------------STEP7------------------------------#
derivative_hidden_layer_1<-derivative_activation(activated_hidden_layer_1)
error_layer_1<- delta_layer_2 %*% t(set_weight_2)
delta_layer_1<- derivative_hidden_layer_1 * error_layer_1
#####################UPDATE##################################
#-------------------------STEP8-----------------------------#
set_weight_3 <-set_weight_3 -
lr*t(activated_hidden_layer_2)%*%delta_final_layer
#---------------------------STEP9--------------------------#
set_weight_2 <-set_weight_2 -
lr*t(activated_hidden_layer_1)%*%delta_layer_2
#--------------------------STEP10--------------------------#
set_weight_1 <-set_weight_1 -
lr*t(input_layer)%*%delta_layer_1
#--------------------------STEP11--------------------------#
bias_3 <- bias_3 - lr* colSums(delta_final_layer)
bias_2 <- bias_2 - lr* colSums(delta_layer_2)
bias_1 <- bias_1 - lr* colSums(delta_layer_1)
#---Storing the accuracy acheived at current epoch-------------#
prediction<-NA
for(i in 1:nrow(final_layer)){
# Among the three values the maximum value indicates the
# instance's corrsponding class
prediction[i]<-(which(final_layer[i,]== max(final_layer[i,])))
}
actual<-as.integer(iris$Species[seq[1:120]])
result<-table(prediction, actual) #Confusion Matrix
accuracy[itr]<- sum(diag(result))/sum(result)
}#--------------------------------------------------------------#
#--------------------------------------------------------------#
#--Prediction function to classify the future test sets--------#predict<-function(test, label){
#Dont Worry, this is just a single forwardfeed using the final #learned weights t.hidden_layer_1<-t(t(test %*% set_weight_1) + bias_1)
t.activated_hidden_layer_1<-activation(t.hidden_layer_1) t.hidden_layer_2<-t(t(t.activated_hidden_layer_1 %*% set_weight_2) + bias_2)
t.activated_hidden_layer_2<-activation(t.hidden_layer_2)
t.final_layer<-activation(t(t(t.activated_hidden_layer_2 %*% set_weight_3) + bias_3)) t.prediction<-NA
for(i in 1:nrow(t.final_layer)){
t.prediction[i]<-(which(t.final_layer[i,]== max(t.final_layer[i,])))
}
t.actual<-label
t.result<-table(t.prediction, t.actual)
colnames(t.result)<-unique(iris$Species)
t.accuracy<- sum(diag(t.result))/sum(t.result)
result<-list(t.result, t.accuracy)
names(result)<- c("Confusion Matrix", "Result")
return(result)
}*
图 7。前馈-反馈传播(图片由作者提供)
是时候测试我们的模型了
*predict(test,test_label)*
对于我们的玩具模型来说,性能相当不错:)。这个模型只给出了一个错误的预测。测试集上的准确率为 0.966。
我们还可以绘制损耗曲线和精度曲线。你可以在这里得到完整的代码。
图 8。训练损失 vs 我们模型的训练精度(作者图片)
唷…
我希望这个神经网络的快速概述能帮助你理解这个非常革命性的算法。我总是乐于接受任何有助于提高本文质量的建议,所以如果你遇到任何错误,请告诉我。谢谢你的阅读和快乐的 R-ing。
其他著名读物
虽然互联网上充斥着关于神经网络的信息,但我仍然会参考一些非常简明扼要的文章。
用代码理解 acgan[py torch]
Python-PyTorch
使用 PyTorch 库和 Python 构建 ACGAN 模型并了解更多信息
法比安·格罗斯在 Unsplash 上拍摄的照片
ACGAN 代表辅助分类器生成对抗网络。该网络由谷歌大脑的一组研究人员开发,并在澳大利亚悉尼举行的第 34 届国际机器学习会议上展示。本文简要描述了论文中所阐述的研究工作以及使用 PyTorch 实现的研究工作。
为什么是 ACGAN?
ACGAN 是一种特殊的 GAN,可以在图像合成方面创造奇迹。你告诉它类别标签,它就能生成图像——全部来自完全噪声!ACGAN 的另一个重要特点是,与以前的方法相比,它生成的图像分辨率相当高。但是任何图像都可以用双线性插值来调整大小,并且它的大小可以增加。没错,它们可以,但它们仍然是低分辨率图像的模糊版本,不会比它们更难以分辨。ACGAN 是第一个提出使用预先训练的初始网络来检查图像可辨性的想法。
[注意—此处构建的 PyTorch 模型是 ACGAN 论文的实现,仅包含生成器和鉴别器。自己动手对照预先训练好的初始网络来检查模型。请在评论中告诉我结果:)
体系结构
与任何 GAN 网络一样,ACGAN 由一个发生器和一个鉴别器组成。然而,在 ACGAN 中,除了噪声 z 之外,每个生成的样本都有相应的类别标签 c ~ C (可用类别),该类别标签帮助模型基于传递的标签来合成图像。生成器 G 使用类别标签和噪声 z 来生成图像。生成的图像可以表示为—
生成的图像( 假 因为它必须被鉴别器标记为假)
发电机架构非常简单。它由一系列反卷积层组成,也称为转置卷积层。迷惑?让我来给你解释一下—
转置卷积层与卷积层相同,但在原始输入中添加了填充。因此,当应用步长为 1 且无填充的卷积时,输出的高度和宽度大于输入的高度和宽度。步长为 2 时,我们可以对转置卷积层执行上采样,就像对步长为 2 的卷积层执行下采样一样。
发生器的转置卷积层由 ReLU 非线性支持。
鉴别器包含一组具有 leaky-ReLU 非线性的 2d 卷积模块,后面是线性层,以及用于其每个输出的 softmax 和 sigmoid 函数,用于检测模型的类别和来源。整个模型可以画为—
ACGAN 模型
既然我们已经通过它们的架构模型定义了发生器和鉴别器,我们将得到损失函数。
损失函数
ACGAN 的损失函数分为两部分
- 被检查源的对数可能性—
源损耗
2.被检查类别的对数可能性—
阶级损失
从上面的损失函数可以明显看出,发生器和鉴别器在这个损失函数上“斗争”。生成器和鉴别器都试图最大化类损失。然而,源损耗是一个极小极大问题。发生器试图最小化源损耗并欺骗鉴别器。另一方面,鉴别器试图最大化源损耗,并试图阻止发电机占上风。
模特[PyTorch]
由于我们已经完成了对 ACGAN 论文的分析,现在我们将使用 CIFAR10 数据集构建模型。你可以从这里下载数据集。事实上,我会将下载合并到培训本身,以摆脱麻烦!该数据集包含 60,000 张 32x32 尺寸的图像。有 10 个类,每个类有 6000 个图像。
发电机,写成一个模块—
ACGAN 的发生器模块
注意,在生成器中,卷积网络已经仔细选择了参数,使得输出张量与来自训练集的张量具有相同的维数。这是必要的,因为两者都进入鉴别器进行评估
鉴别器,也写成一个模块—
鉴别器模型
现在,让我们开始训练!
为了进行训练,我将 epochs 的数量设置为 100。学习率设置为 0.0002,批量设置为 100。
理想情况下,为了正确的图像合成,历元的数量应该更多。例如,我已经将它设置为 100
培训模式
结果!
让我们来看看这个小实验的结果
第一个时期的图像(噪声)
第一历元图像
上一个时代的图像—
第 100 代图像
很大的进步,是吧?甘斯的妙处在于,你可以通过影像看到模特的训练。随着模型慢慢了解分布情况,您可以看到跨时代的结构正在形成!你还在等什么?为模型编写自己的代码;用你自己的方式解决问题。如果可以的话,即兴创作解决方案——我们会看看是否能以你的名字命名;)
如果你卡住了,请在评论中告诉我!来帮忙了:)
查看我的博客以获得更快的更新,不要忘记订阅更多高质量的内容:D
https://www.theconvolvedblog.vision/
了解用于决策树的 AdaBoost
用 R 实现
决策树是流行的机器学习算法,用于回归和分类任务。它们之所以受欢迎,主要是因为它们的可解释性和可表达性,因为它们模仿了人脑做出决策的方式。
在我以前的文章中,我已经介绍了一些决策树的集成方法,其目的是将一堆弱分类器转换成一个更强的分类器。在这里,我将详细阐述 Boosting 方法,这是算法在每次迭代中从错误中“学习”的一种方法。更具体地说,我将一步一步地解释 Adaboost 背后的思想以及如何用 r 实现它。
Adaboost 背后的理念
Adaboost 和 bagging 方法(包括随机森林)之间的主要区别在于,在过程结束时,当迭代期间构建的所有分类器都被要求为新观察的目标投票时,将会有比其他树投票更重的树。这些是在所有迭代中表现最好的树(因此,它们表现出很少的错误分类)。比方说,在一天结束的时候,有些树会比其他树说得更多。这种“发言权的重要性”在整个迭代过程中被测量(和更新)。
除此之外,Adaboost 的其他重要特性,如预期的那样,是它从过去的错误中学习的能力,因为在每次迭代中,下一个分类器是基于过去的错误分类错误建立的。
现在让我们看看算法是如何具体工作的。
该算法
假设我们有一个数据集,有 N 个观察值,P 个协变量和一个二元目标变量 y=1,-1。这将是我们的火车布景。
你可以用这个框架设计任何你感兴趣的任务(即,一封电子邮件是否是垃圾邮件,明天的天气是否晴朗……)。
- 我们要做的第一件事是给每一行分配一个权重,这表明该观察值被很好地分类有多重要。我们将这些权重初始化为 1/N,这样在第一次迭代时,当训练第一个分类器时,它们不会产生差异。
- 现在,我们在数据集上训练我们的第一个分类器 G(x ),然后检查哪些是错误分类的观察值,因为我们想要计算总误差,它不同于标准的错误分类误差,因为它是加权求和:
如您所见,我们有一个自标准化的加权误差总和(当目标与拟合值不同时)。请注意,由于其构建方式,总误差将始终介于 0 和 1 之间。有了这个量,我们就可以计算出我们所说的‘话语权的重要性’:
如你所见,误差越大,最后那棵树的投票就越不重要。我们也可以形象化地描述它:
error = seq(0,1,0.001)
alpha = log((1-error)/error)
plot(error,alpha, type='line')
- 有了这个度量α,我们现在可以更新权重,使得对应于错误分类的观察值的权重在下一次迭代中将更高。通过这样做,当我们将在新的加权数据集上拟合下一棵树时,错误分类这些观察值的成本将会更高,并且树将会非常小心地不再错误分类它们(因为这将意味着更高的成本)。因此,对于所有的观察值,i=1,2,..,N,我们更新第 I 个权重如下。
- 如果我们重复这个过程 M 次迭代,在一天结束时,我们将有 M 个分类器,每个分类器有一个加权投票,并且,如果我们想要预测新观察 x*的目标,公式是:
因此,将会有一些树的输出在最终决策中具有很大的决定性(-1 或 1),而其他的树则可以忽略不计。
现在让我们来看看这个过程其余部分的一个非常简短的实现:
data = data.frame( X=c(rnorm(100,0,1),rnorm(100,1,1)), Y=c(rep(0,100),rep(1,100) ) )data$Y =factor(data$Y)
model =adaboost(Y~X, data, 10)
pred =predict( model ,newdata=data)
我们来看看模型的总结和它的预测误差(等于训练集中的误分类误差):
print(pred$error)
print(model)
我们还可以检索最终的“重要性”权重:
model$weights
如你所见,有一些树(像第一棵树)的最终投票非常重要,而其他树(像最后一棵树)则不那么重要。
最后,我们可以将其误差与单棵树的误差进行比较:
single_model=tree(Y∼X, data)
summary(single_model)
如你所见,我们的提升分类器比单一的树分类器更强。
结论
集成方法是强大的技术,可以大大提高决策树的预测准确性。然而,这些方法的警告是,它们使得呈现和解释最终结果变得不太容易。事实上,正如我们在开始时所说的,决策树最显著的特征是它们的可解释性和易于理解性,在一个看起来像“黑盒”的算法世界中,这是一个重要的价值。尽管如此,还是有一些视觉上的选择,如果集合多个树能提高那么多的准确性,那肯定是值得的。
用代码和例子理解 Python 中的高级函数!
通过代码和示例详细了解 python 中的匿名函数和高级函数及其实际实现。
萨法尔·萨法罗夫在 Unsplash 上拍摄的照片
Python 是一种优雅的编程语言,它为用户提供了许多简化代码和缩短代码长度的工具。它是一种面向对象的高级编程语言,早在 1991 年就发布了,具有高度的可解释性和高效性。
我最初是从 C、C++和 Java 这样的语言开始的。当我终于遇到 python 的时候,我发现它相当优雅,简单易学,易于使用。对于任何人来说,Python 都是开始机器学习的最佳方式,甚至对于以前没有编程或编码语言经验的人也是如此。
由于其数据结构、条件循环和函数的简单性,它提供了广泛的通用性来处理各种问题。匿名函数和高级函数是我们今天要讨论的主题,但基本问题是——什么是函数?
函数是写在程序中的一段代码,这样它们可以被多次调用。函数的主要用途是在同一个程序中可以多次重复调用它,而不需要一遍又一遍地编写相同的代码。然而,你也可以用它来为你的程序提供更多的结构和更好的整体外观。
使用关键字 'def,定义函数,可以使用已定义或未定义的参数调用这些函数。当您调用特定的函数时,python 编译器会解释将要返回的任何值。
如果您的代码采用这个由几行代码组成的块,并且只用一两行代码就执行了它,会怎么样呢?
这个任务可以通过 python 中的匿名函数来执行。让我们在下一节更深入地理解这个概念。
阿诺德·弗朗西斯卡在 Unsplash 上拍摄的照片
匿名函数:
在 Python 中,匿名函数是定义时没有名字的函数。在 Python 中,普通函数是使用 def 关键字定义的,而匿名函数是使用 lambda 关键字定义的。因此,匿名函数有时也被称为 lambda 函数,它们通常可以互换使用。
该函数的语法如下:
lambda arguments: expression
使用 lambda 函数的主要优点是执行一个 lambda 函数,该函数计算其表达式,然后自动返回其结果。所以总有一个隐式的 return 语句。这就是为什么有些人把 lambdas 称为单表达式函数。在大多数情况下,它对于简化代码非常有用,并且是编程语言不可或缺的一部分。
大多数情况下,lambda 函数是好的,但是如果使用这些函数会使单行代码比预期的长,并且用户很难阅读,那么就应该考虑不使用它们。基本上,当代码的可读性降低时,您应该考虑不使用 lambda 函数。
让我们了解如何准确地使用 lambda 函数中的代码来为我们造福。为了有一个清晰的概念理解,我将列出一些问题陈述,以及它们的带有普通函数和 lambda 函数的代码块解决方案。
问题陈述:
使用函数打印数字的平方。
具有定义功能的代码:
输出:
25
上面的代码块和输出代表了如何通过使用一个普通的 def 函数来计算问题语句来编写下面的代码。
带有 lambda 函数的代码:
输出:
25
在 lambda 函数的帮助下,同样的问题语句可以在两行代码内解决。这就是 lambda 函数简化问题陈述的方式。
Lambda 函数通常与其他函数一起使用,在几行代码而不是一段代码中解决复杂的任务。这些功能即过滤、映射和减少。让我们用单独的问题陈述来分别分析每一个问题。
照片由 Cookie 在 Unsplash 上的 Pom 拍摄
1.过滤器:
顾名思义,filter()函数将创建一个新的列表,只存储满足特定条件的元素,并过滤掉剩余的值。
filter()操作接受一个函数对象和一个 iterable,并创建一个新列表。只要满足条件并返回 true,就会执行成功的计算。
问题陈述:
只打印元素列表的偶数。
具有定义功能的代码:
输出:
[2, 4]
在上面的代码块中,函数接受一个数字列表的输入。然后我们遍历列表,使用条件语句只存储给定列表中的偶数。该函数最终返回包含所有偶数的列表。
带有 lambda 函数的代码:
输出:
[2, 4]
在一行代码中,我们可以借助上面的代码从 iterable 列表中筛选出偶数。返回的输出与前面冗长的函数完全相同。
2.地图:
map()函数遍历给定 iterable 中的所有项,并对每个项执行我们作为参数传递的函数。这类似于 filter()函数。
它可以用来获得条件列表上的布尔值 true 和 false,或者只是将每个元素映射到其各自的输出。让我们用一些代码更详细地理解这一点。
问题陈述:
打印列表中每个元素的方块。
具有定义功能的代码:
输出:
[1, 4, 9, 16, 25]
在上面的代码块中,该函数接受一个类似于前一个问题的数字列表的输入。然后我们遍历列表,计算存储在列表中的每个元素的平方。该函数最后返回包含每个元素的所有平方数的列表。
带有 lambda 函数的代码:
输出:
[1, 4, 9, 16, 25]
map 函数在一行代码中计算并解决问题陈述。列表中的每个元素都映射有各自的方块。
3.减少:
与前两个函数(即 filter()和 map())不同,reduce 函数的工作方式略有不同。它遍历可迭代数字列表,并继续返回一个值。
这个函数在用简单代码而不是较长的代码块执行列表的加法或乘法等计算时非常有用。reduce()函数一次计算列表中的两个元素,直到完成整个 iterable。
问题陈述:
打印列表中所有元素的乘积。
具有定义功能的代码:
输出:
120
在上面的代码块中,该函数接受一个类似于前两个问题的数字列表输入。然后我们遍历列表,计算给定列表中所有数字的乘积。该函数最终返回评估列表后获得的最终产品的输出。
带有 lambda 函数的代码:
输出:
120
reduce()函数需要从 python 3 中的 functools 模块导入。上面的代码块一次执行两个元素的顺序乘法,直到完成整个列表的计算。
结论:
在本文中,我们简要介绍了 python,并对函数在 Python 编程语言中的作用有了基本的了解。
然后,我们进一步讨论了 python 中的匿名函数,并通过一些代码和示例了解了其中可用的每个高级函数选项。
如果你对今天的话题有任何疑问,请在下面的评论中告诉我。我会尽快回复你。如果我错过了什么,请随时告诉我。您希望我在以后的文章中介绍这一点。
看看我的其他一些文章,你可能会喜欢读!
获得一个 GPU 是深度学习的必备条件吗?了解 GPU 及其优势,并探索…
towardsdatascience.com](/do-you-really-need-a-gpu-for-deep-learning-d37c05023226) [## 机器学习和数据科学项目的 10 步终极指南!
详细讨论构建您的机器学习和数据科学项目的最佳方法…
towardsdatascience.com](/10-step-ultimate-guide-for-machine-learning-and-data-science-projects-ed61ae9aa301) [## 分步指南:使用 Python 进行数据科学的比例采样!
了解使用 python 进行数据科学所需的比例采样的概念和实现…
towardsdatascience.com](/step-by-step-guide-proportional-sampling-for-data-science-with-python-8b2871159ae6) [## 2020 年及以后最受欢迎的 10 种编程语言
讨论当今 10 种最流行的编程语言的范围、优缺点
towardsdatascience.com](/10-most-popular-programming-languages-for-2020-and-beyond-67c512eeea73) [## OpenCV:用代码掌握计算机视觉基础的完全初学者指南!
包含代码的教程,用于掌握计算机视觉的所有重要概念,以及如何使用 OpenCV 实现它们
towardsdatascience.com](/opencv-complete-beginners-guide-to-master-the-basics-of-computer-vision-with-code-4a1cd0c687f9)
谢谢你们坚持到最后。我希望你们都喜欢这篇文章。祝大家有美好的一天!
用 PyMC3 了解飞机事故趋势
图片由 Free-Photos 来自 Pixabay
使用 PyMC3 直观地探索历史上的航空事故,应用频率主义者的解释并验证变化的趋势。
前言
今年 8 月 7 日,一架从迪拜(阿联酋)飞往 Kozhikode(印度喀拉拉邦)执行遣返任务的印度航空快运飞机在暴雨中滑出跑道,坠入山谷[1]。
随后 35 英尺的坠落将飞机摔成两半。该航班共搭载了 180 名乘客,其中 18 人在事故中丧生。其余 172 人不同程度受伤,接受治疗2。
对这一可怕事故的官方调查自然将是一项事实调查任务,并将试图弄清楚哪里出了问题,谁该负责。
动机
在这个故事之后,我开始谷歌最近的飞机事故,以了解背景,并从全球角度看待这些事件。
这次搜索让我找到了许多网页,上面有飞机坠毁的照片和视频,坠毁统计表,事故调查报告,以及不同航空业专家在此类灾难性事故后的原话。
这次调查的底线是,我们正处于一个日益安全的飞行环境中。监管、设计、机械和电子安全措施比以往任何时候都更加严格,从而使飞行成为一种相对更安全的交通方式。
但是我想亲自用这些数字来验证这个结论。
这个练习的动机问题是—
与过去相比,最近一段时间飞行变得相对安全了吗?
数据源
我在维基百科和 T2 国家运输安全委员会上查看了公开的空难数据,并创建了一个数据集来满足这次演习的需要。
整个练习和数据集可以在我的 GitHub 资源库中找到。
现在切换到第一人称复数……
工作工具
为了回答这个激励性的问题,我们把这个任务分成两部分—
- Python 中的探索性数据分析(EDA)。
- Python 中的概率编程(PyMC3)。
探索性数据分析
在这一部分中,我们回顾了过去的飞机坠毁事件,这些事件构成了我们进行分析的时间序列。一些需要记住的事情-
- 《国际民用航空公约》将飞机事故与飞机事故区分开来。区别主要在于是否发生了死亡事故。
- 在这次练习中,我们的重点仅限于事故的发生,而不是其原因。
- 我们研究了从 1975 年到 2019 年的商业飞机事故。
事故和死亡趋势
图 1-从 1975 年到 2019 年每年的事故和死亡人数。
从历史时间序列来看,我们可以直观地感受到自 1978 年以来每年事故数量的下降。1987 年至 1989 年期间,事故数量似乎略有上升,此后数量稳步下降。2017 年是事故发生率最低的一年,也是航空史上最安全的一年。2017 年后,这些数字似乎略有增加。
另一个明显的趋势是死亡人数随着时间的推移而下降。20 世纪 70 年代和 80 年代是飞行的危险时期,平均每年有 2200 起飞机事故导致死亡。但是随着时间的推移,我们看到这个数字已经急剧减少。
当这种下降趋势被放在航空旅客数量上升的背景下看待时(图 1 中绿色阴影区域),我们对航空安全有了更好的了解。
每百万乘客的死亡人数
图 2——每年百万乘客出行中的死亡人数
当从航空旅客人数上升的角度来看死亡人数的下降时,我们会看到一个明显的下降趋势。每年乘飞机旅行的每百万乘客中的死亡人数已经从百万分之五急剧下降到百万分之一以下。
(免责声明:贝叶斯人,准备好那撮盐)
每起事故死亡人数
图 3-每起飞机事故死亡人数的变化
飞机安全的另一个衡量标准是每起事故的死亡人数。虽然可能有许多外部因素(外部因素)影响特定事故中的死亡人数——天气、碰撞性质、一天中的时间等。——我们仍然将这一指标视为对飞机安全性的粗略评估。
1995 年以后,趋势似乎略有下降,但从图表中并不能立即观察到。我们还看到,1985 年、1996 年、2014 年和 2018 年是涉及重大撞车事故的致命年份,因为每次撞车事故的平均死亡人数很大。
变动率
图 4 —事故数量的年度百分比变化
在我们开始动机问题的概率测试之前,最后一个证据是事故的年变化率。
如果我们真的生活在安全的时代,那么我们期望图表显示一系列连续增加的绿色条。这样的窗口只在 1979-80 年、1980-84 年、1999-00 年、2006-07 年和 2013-14 年观察到。从 1980 年至 1984 年和 1996 年至 2000 年,可以看到相对安全旅行的延长期。
如果我们看一下 1995 年以后的变化率,我们会发现事故同比有很大程度的下降(红色柱很少,绿色柱更多)。
似乎一些外部因素(如飞机设计的变化、民航规章、更好的空中交通管制技术等。)可能导致了 1995 年以后的这种下降。
概率规划
从我们的数据研究中,我们发现每十年的飞机事故数量持续下降,我们用一些统计方法验证了这一趋势。
我们还看到,1995 年可能是航空业的一个转折点。我们如何验证这个假设?
在有限的数据和事件的不可重复性(让我们假设我们无法模拟这些事故一百万次)的情况下,这样做的一个有趣的技术是使用概率技术,如马尔可夫链蒙特卡罗(MCMC)。
实现这些技术的方法之一是借助 Python 中的 PyMC3 库。
快速入门
PyMC3 是 Python 中的一个库,帮助我们进行概率编程。这并不意味着编程是概率性的(它仍然是一个非常确定的过程!相反,我们采用概率分布和贝叶斯方法。
这种技巧是建立在一种的世界观之上的。我们从一个关于某个过程或参数的信念(称为先验概率)开始,并在几千次运行(又称随机抽样)后更新这个信念(称为后验概率)。这种方法与频率主义者看待事物的方式相反(就像我们在 EDA 中所做的*)。*****
这个过程的第二个基础是 马尔可夫链蒙特卡罗 (MCMC)的随机抽样方法。这是一套算法,允许我们从先验概率分布中取样,并生成数据来测试我们的先验信念并更新它们。
PyMC3 网页上提供的文档和Susan Li的这个实践方法对于高层次理解库和技术是极好的。坎姆·戴维森-皮隆写的《黑客的贝叶斯方法》这本书真的很有帮助,如果你想动手的话。
好吧,让我们测试一下
我们从建立我们对事故的先验信念开始—
飞机事故遵循怎样的分布?
这里我们假设事故遵循泊松分布。
**P(x|lambda) = (lambda^x)*(exp^-lambda)/(lambda!)x: number of accidents
lambda: rate of occurrence of the accident**
发生率会是多少?
给定我们的初始假设,我们进一步假设这个发生率大约是整个数据集的平均发生率的倒数。
换句话说,
**lambda = 1/(mean of number of accidents from 1975 to 2019)**
最初的转折点会是什么?
转折点是前一年发病率高,后一年发病率低。我们最初假设,从 1975 年到 2019 年的每一年都有相等的概率(取自离散的均匀分布)被认为是一个转折点。**
有了这些先验的信念,我们实例化了这个模型—
**import pymc3 as pm
import arviz as azyears = np.arange(1975, 2020)
with pm.Model() as accident_model:
alpha = 1/df.loc[df.year>=1975].num_crashes.mean()
# Setting the prior belief for the inflection point
change_point = pm.DiscreteUniform(
'change_point', lower=1975, upper=2020)
# Setting prior belief for the rate of occurrence, ie, lambda, before and after inflection
rate_before = pm.Exponential('before_change', alpha)
rate_after = pm.Exponential('after_change', alpha)
# Allocate appropriate Poisson rates to years before and after current
rate = pm.math.switch(change_point >= years, rate_before, rate_after)accidents = pm.Poisson("accidents", rate, observed=df.loc[df.year>=1975].num_crashes)**
我们用不掉头采样器(螺母)对这些分布进行了至少 10,000 次采样—
**with accident_model:
trace = pm.sample(10000, return_inferencedata=False)**
测试结果
图 5-更新我们对可能的变化点和发生率的先前信念的结果。
我们看到,在采样 10,000 次后,我们最初认为所有年份都有同等机会被视为转折点的想法得到了更新。结果表明,1997 年(而不是 1995 年)最有可能成为航空事故史上的转折点。
并且已经更新了最初的假设,即发生率是 45 年平均值的倒数。1997 年被认为是一个转折点,因为事故发生率从大约每年 300 起变成了每年 165 起!
那么这些预测有多大把握呢?
图 6 —预测值的不确定性
概率编程的 USP 是,预测是用一撮盐做出来的!与频率主义者的预测不同,贝叶斯方法的预测带有不确定性(更现实)。**
我们的模型显示,94%的高密度区间(HDI)在 1996 年和 1999 年之间,1997 年是平均值。换句话说,1997 年成为转折点的概率较大。
在这一转折点之前,类似的 94%人类发展指数发生率为每年 295 至 312 起事故;1997 年以后的事故每年在 158 至 172 起之间。
最近的过去
由于我们的激励问题仅限于“最近的时间”,我们将该模型应用于 2000 年至 2019 年的数据(假设最近 20 年足够近)。**
图 7-2000 年至 2019 年间事故的模型结果
我们观察到,2012 年很可能是一个转折点(94%的人类发展指数是 2010 年至 2013 年),2012 年之前的事故率接近每年 180 起,2012 年之后大约每年 120 起。
图 8-2000 年至 2019 年间事故模型结果的不确定性
裁决
所以通过进行这个小练习,我能够满足我的好奇心并回答这个激励性的问题—
如果每年的低航空事故率是航空安全的唯一指标,那么在 1997 年后,事故率大幅下降,在过去 20 年中,2012 年后数字进一步下降。
尽管每年的事故数量很少,但与 20 年前相比,现在飞行相对更安全。
参考
再见!
理解 AlphaGo:人工智能如何思考和学习(高级)
是时候了解卷积神经网络、深度学习以及我们的现代人工智能如何赢得顶级围棋选手的比赛了…
“作为一名技术专家,我看到人工智能和第四次工业革命将如何影响人们生活的方方面面。”
费-李非
本文将从深度学习开始,深入到 DeepMind AI 的架构,就像 Alpha Go 一样。到文章结束的时候,我们应该能看懂很多人工智能领域最前沿的论文了,像 Alpha Go Zero 的原论文。蒙特卡罗树搜索(MCTS)和卷积神经网络是我们在理解 Alpha Go 如何工作之前应该熟悉的两个基本概念。如果你有兴趣了解更多,决策树、状态机、强化学习和蒙特卡罗树搜索等概念在了解 alpha go:AI 如何思考和学习(基础)中有解释。
这篇文章的目的是让人们了解 Alpha Go 是如何工作的,但它也可能会提高人们对人工智能领域的兴趣,因为人工智能领域的信息太多,无法包含在一篇中型文章中。我们将要学习更高级的材料,所以要准备好一些可能更难理解的东西。我会尽我所能,用最简单的方式解释这些概念。
我们开始吧!
深度神经网络的灵感来自我们大脑的理论,图片来自 Pixabay
受数学和神经科学启发的深度神经网络
“这是最糟糕的时候,其他人都在做不同的事情。”
——本吉奥,CIFAR 项目的联合主任
神经网络简史
1943 年,神经生理学家沃伦·麦卡洛克和数学家沃尔特·皮茨基于称为阈值逻辑单元(TLU) 的数学算法创建了计算模型,以描述神经元可能如何工作。在 20 世纪 50 年代计算机变得更加先进之前,模拟神经网络是可能的。
在 2000 年之前,这被认为是最糟糕的研究领域之一。LeCun 和 Hinton 以不同的方式提到了在这一时期他们的论文是如何由于他们的主题是神经网络而被拒绝发表的。人工神经元被认为是无用的,因为它的单个神经元甚至不能解决 XOR 逻辑。
这项研究由加拿大高级研究所资助。在该领域所有研究人员的不断努力下,Krizhevsky、Sutskever 和 Hinton 凭借卷积神经网络(CNN)赢得了 2012 年的 ImageNet 竞赛,卷积神经网络是 Yann LeCun 在 1998 年首次创建的模型。此后,深度神经网络几乎出现在每一篇论文中。
神经网络之所以在诞生后的很长一段时间里如此成功,还有其他原因。首先,这些模型严重依赖于计算能力,随着这些年来我们的计算机变得越来越先进,它们最终变得更加可行。更重要的是,为了充分训练这种先进的模型,需要大量的数据。在互联网与社交媒体、电子商务和移动设备一起受到消费者欢迎后,每天都会产生大量数据,这为每个人创造大量数字数据增加了更多机会。这些新技术也创造了对计算机视觉(CV)和自然语言处理(NLP)应用的需求。
在人工智能领域有两种对立的信念——连接主义和象征主义。联结主义认为认知科学的方法希望解释智能行为。另一方面,象征主义依赖于基于数学和逻辑运算的推理。虽然许多数学模型成功地完成了给定的任务,但也有足够多的研究表明,基于认知科学的方法确实加速了人工神经网络的学习过程。
连接主义与象征主义,图片来自 Pixabay
人工神经网络
术语“深度学习”是一个涉及深度神经网络的领域,意思是具有一个或多个隐藏层的人工神经网络。人工神经网络是一个有向无环图(DAG) 由人工神经元的连接层组成。这些人工神经元也被称为“感知器”,因此人工神经网络有时被称为多层感知器(MLP)。这些值将被输入到输入层,这是网络的第一层,结果将从输出层出来,这是最后一层。
人工神经网络(ANN),有时也称为多层感知器(MLP)
我们可以将多层感知器视为一种投票方案,为了得出一个决定,输入层的每个感知器向下一层的感知器发送一个加权投票,再下一层……直到投票在输出层的感知器中完成。激活函数通常在中间很陡,以确保激活值位于两端,从而接近二进制判决值。这里是我们可以可视化人工神经网络如何分类数据的地方。
人工神经网络如何工作
正向传播&激活函数
在每一层中,有许多相互连接的感知器。感知器之间的机制可以由以下术语定义:
- 激活( a ) :感知器的激活是通过对前一层感知器的激活求和,加上一个偏差,然后通过激活函数进行转换来计算的。
- Bias ( b ) :每次感知器接收到一个值,就会对该值应用一个 Bias。
- 权重( w ) :感知器的激活值将被乘以一个权重并相加。
- 激活函数( σ ) :从激活函数计算每个感知器的激活值。
感知器背后的数学
有几种类型的激活功能。Sigmoid 函数(σ),经常与学习曲线有关,是最基本的一个。每个激活功能都有其特点。整流线性单元(ReLU)是最受欢迎的一种。
每个感知器会将其值传播到前进层的感知器中,最终到达输出层。这个过程被称为正向传播。
人工神经网络的前向传播
反向传播&成本函数
前向传播基于权重、偏差和激活函数,但是是什么决定了这些值呢?激活函数是预先选择的,但是对于大型神经网络,不可能手动选择适当的权重和偏差。
在机器学习领域,模型应该自己从数据中“学习”,这个学习过程也称为“训练”。通常,数据被分成 2 个不同的集合——训练集合和测试集合。训练集用于将模型“训练”到更成熟的状态,然后性能将由测试集进行评估。****
传统算法与机器学习的比较
有许多不同的方法来“训练”一个人工神经网络,但最流行的方法是使用反向传播。
在反向传播之前,神经网络的权重和偏差通常以正态分布随机初始化。然后,神经网络将执行前向传播。由于权重和偏差是随机初始化的,所以第一次正向传播的结果通常会相差很远。然后使用成本函数来计算预期结果和神经网络输出之间的差异。计算出差异后,它将用于调整前一层的权重和偏差。该过程在层中向后传播,因此它被称为“反向传播”。
人工神经网络的反向传播
这里有一个关于反向传播的更正式的教程,因为它需要一些高等数学来解释。神经网络的解释和代码示例可以在这里找到,这里作者使用矩阵运算来模拟 Python 中的神经网络。
反向传播解释
卷积神经网络(CNN)
为了以更好的效率处理图形数据,Yann LeCun 在 1989 年发明了卷积神经网络。该网络计算 2D 阵列的空间信息。卷积神经网络也非常适合分析其他空间信息重要的 2D 数据,包括棋盘。
卷积神经网络由 3 种类型的层构成——卷积层、池层和全连接层。这些具有不同形状和大小的层在不同的主题上会有不同的表现。对卷积神经网络的研究通常涉及调整这些层及其组成,以优化目标数据集的性能。
卷积层(conv)
卷积层通常作为卷积神经网络的第一层出现。这些类型的图层将使用过滤器扫描源图层,并将总和放入目标图层。一些滤波器擅长检测边缘,一些擅长其他任务,更多关于不同类型卷积滤波器的细节及其在计算机视觉中的应用可以在这里找到。
卷积层(3x3)
池层(pool)
池层遍历源层,并在有界区域内选择一个特定的值。该值通常是该区域内的最大值、最小值和平均值。将信息缩小也称为“缩减采样”。
池层(2x2 最大池)
全连接层(fc)
全连接层本质上是一个多层感知器,它有时被称为“softmax”,本质上做一些被称为“加权和”的事情。
全连接层只是一个人工神经网络
卷积神经网络最常用于计算机视觉领域,它影响了许多领域,如电子商务、金融科技、游戏人工智能、癌症检测、整形外科、精神病学、野火检测等。这里有一篇关于计算机视觉如何影响电子商务的文章和另一篇关于计算机视觉中一些很酷的前端框架的文章。
Alpha Go Zero,用机器学习掌握围棋游戏
“太完美了,简直完美无瑕,毫不留情。…我想我这辈子也追不上它了。”
—柯洁(围棋世界冠军)在输给 Alpha Go Zero 三局后
恭喜你!!!我们已经到了这篇文章的目的所在。现在我们将准备好了解传说中的国际象棋和围棋人工智能是如何从头到脚工作的。
设计 Alpha Go Zero 的架构
Alpha Go Zero 由卷积神经网络和蒙特卡罗树组成。它通过强化学习算法进行自我游戏训练。
阿尔法如何归零转弯
有几个术语我们应该熟悉一下。由于 Medium 不支持大多数字母的下标,所以下标由“_”后括号内的字母表示,类似于 LaTeX。
- 状态(s) :游戏的状态用 s(T)表示,从 s(0)到 s(T),其中 s(T)为终止状态。
- 蒙特卡罗树(α) :蒙特卡罗树α_(θ)用于决定游戏的下一个状态。
- Move (a) :每个状态 s_(t)的移动 a_(t)由搜索概率π_(t)决定。
- 搜索概率(π) :搜索概率π_(t)用于确定在状态 s_(t)下移动 a_(t)。
- 卷积神经网络(f) :卷积神经网络 f_(θ)用于通过分析棋盘输出价值向量 v 和策略向量 p。
- 值向量(v) :值向量 v_(t)表示当前玩家在位置 s_(t)获胜的概率
- 策略标量(p) :策略标量 p_(t)表示移动的概率分布。
- 赢家(z) :赢家 z 被传播回去训练模型。
Alpha Go Zero 完全通过自我对弈进行训练,仅使用 1 个卷积神经网络,不同于原始 Alpha Go 需要 2 个卷积神经网络,并借用专业人类对弈的信息。
Alpha Go Zero 将游戏棋盘作为卷积神经网络的原始输入。然后网络输出一个矢量 v 和一个标量 p 。然后,蒙特卡罗树用于计算搜索概率π,该概率用于确定游戏下一阶段的移动。最后,根据游戏规则确定一个获胜者 z,并使用它通过强化学习来训练模型。
到现在为止,我们已经知道了 Alpha Go Zero 是如何工作的!!!更重要的是,我们已经步入了人工智能的世界。
人工智能的现在和未来
“记住我的话——人工智能比核武器危险得多”
—特斯拉& Space X 首席执行官、OpenAI 联合创始人埃隆·马斯克(Elon Musk)等等……
我们在人工智能方面的研究处于一种有趣的状态。在机器学习之前,所有的人工智能都是硬编码的,以我们期望的方式表现。机器学习允许人工智能独立地改进自己,有时会产生意想不到的行为。研究人员观察到,通过将人工智能主体放入环境中,并以一定的规则奖励它们,人工智能主体的智能行为逐渐增加。这可能是迷人的,但想想也很可怕。
开启 AI 多智能体捉迷藏,智能体,学会了在进化算法下的智能行为
游戏环境中的研究就像数学和物理。就其本身而言,这种研究可能只是好奇想知道和尝试的乐趣,但缺乏任何坚实的价值。然而,它们将为其他更具应用性的科学和工程提供必要的基础。未来掌握在你我手中,我们一起将明天变得更加美好。
作者的笔记
我花了相当多的时间才完成文章的第二部分。我忙于学业和找工作。我是人机交互(HCI)专业的硕士生,但我对许多不同的领域都感兴趣。人工智能就是其中之一。我希望这篇文章对你有所帮助。另外,我非常需要一份工作,所以如果你有适合我的工作,请告诉我。写作也是我的爱好,因为我喜欢把事情弄清楚并与他人分享,所以如果你在 Medium 上跟随我,你会看到更多这样的事情。
“未来掌握在你我手中,我们一起将明天变得更加美好。”
—黄慎,本文作者
用例子理解和选择正确的概率分布
概率与统计
举例说明最常见的离散概率分布
作者图片
概率分布
概率分布是一种数学函数,它描述了获取事件可能值的可能性。概率分布可以是离散的,也可以是连续的。在离散分布中,数据只能取某些值,而在连续分布中,数据可以取指定范围(可以是无限的)内的任何值。
有各种各样的离散概率分布。离散概率分布的使用取决于数据的属性。例如,使用:
- 二项式分布,用于计算一个过程的概率,在该过程中,每次试验只可能出现两种可能结果中的一种,例如掷硬币。
- 超几何分布,以寻找在没有替换的情况下 n 次抽奖中 k 次成功的概率。
- 泊松分布,用于衡量给定时间段内给定数量的事件发生的概率,例如图书馆每小时的图书借阅次数。
- 几何分布,用于确定在第一次成功之前进行指定次数试验的概率。
二项分布
二项分布可能是所有离散分布中最广为人知的。这种分配有两种可能的结果。使用二项分布的一个典型例子是抛硬币。抛硬币只有两种可能的结果:正面或反面,每种结果都有相同的 1/2 概率。我们来看看二项分布什么时候可以用!
二项分布的主要特征:
- 这个实验包括 n 次相同的试验。
- 每次试验只有两种可能的结果,即成功或失败。
- 这些试验是相互独立的。
- 用 p 表示成功的概率,它在两次试验之间保持不变,用 q = (1 — p)表示在任何试验中失败的概率。
作者图片
例子
问:某仓库发运十台印刷机,其中四台对某公司有缺陷。这家公司随机挑选了五台机器,如果五台机器都没有缺陷,就接受发货。
在抽样替换时,找出公司接受货物的概率。
我要用上面的公式:
试验次数(n)为 5,得到有缺陷机器的概率(p)为 4/10,所以 q 为 6/10,x = 5。
作者图片
超几何分布
超几何分布是一种非常类似于二项式分布的概率分布。超几何分布和二项式分布都描述了一个事件在固定数量的试验中发生的次数。对于二项分布,每次试验的概率保持不变。相比之下,在超几何分布中,每次试验都会改变后续试验的概率,因为没有替代。
超几何分布的主要特征:
- 考虑一组 N= N1 + N2 的相似物体,其中 N1 属于两个二分类之一,而 N2 属于第二类。
- 从这 N 个对象中随机选择的 n 个对象的集合,并且没有替换。
作者图片
例子
问:让我们稍微改变一下之前的问题。如果我们现在不更换样品,公司接受货物的可能性有多大?
我们知道,印刷机的总数 N 是 10,随机选择进行测试的机器数 N 是 5。让我们定义 N1 是无缺陷的,N2 是有缺陷的,那么 N1 =6,N2= 10,N1 =4。为了让公司接受这批货,我们不能有任何有缺陷的机器。选择所有无缺陷机的方式数为 6C5,选择 0 缺陷机的方式数为 4C0。
作者图片
泊松分布
泊松分布帮助我们预测特定事件在某一时间间隔内发生的概率。
泊松分布的主要特征:
- 在不重叠的时间间隔内发生变化的次数是独立的。
- 在长度为 h 的足够短的间隔内恰好发生一次变化的概率约为λh,其中λ>0。
- 在足够短的时间间隔内发生两次或两次以上变化的概率基本为零。
作者图片
请注意,泊松分布是二项式分布的一种极限形式。对于大的 n,我们有 p=λ/n.
例子
问:假设某种流感疫苗出现副作用的概率为 0.005。如果 1000 人接种,求最多一人患病的大概概率。
由于 n=1000 是一个很大的数,我们可以用泊松近似二项分布来求解,其中λ =pn = 0.005 * 1000 =5。
P(x≤1) = P(x=0)+P(x=1)
作者图片
问:电话在某住所接听为泊松过程,参数λ= 2/小时。如果黛安洗了 10 分钟的澡,电话在这段时间内响起的概率是多少?
假设每 60 分钟有两次通话,我们首先计算每 10 分钟我们期望的通话次数,λ=2 * 10 / 60 = 1/3。现在我们要计算在这 10 分钟内至少接到一个电话的概率,所以本质上我们要计算 P(X≥1),可以写成 1 — P(X=0)。
作者图片
几何分布
几何分布表示在第一次成功之前进行指定次数试验的概率。遵循几何分布的一个典型问题是确定一枚硬币在第一次正面朝上之前正面朝上的次数。
几何分布的主要特征:
- 考虑一系列独立的试验,每个试验都有两种可能的结果,成功或失败。设 p 为成功的概率。定义随机变量 X 为第一次成功的试验。
- 理论上,试验的次数可能会永远持续下去。必须至少有一次审判。
作者图片
例子
问:一台机器生产一件次品的概率是 0.01。每件产品在生产时都经过检查。假设这些是独立的试验,并计算必须检查至少 100 个项目才能找到一个有缺陷的项目的概率。
使用上面的公式,P(X≥100) → P(X>99)
作者图片
结论
概率分布是统计学的基础,就像数据结构对于计算机科学一样。在本文中,我总结了几种最常见的离散概率分布的用例。这只是概率分布旅程的开始。要了解更多关于不同概率分布的信息,请看这张令人难以置信的所有单变量分布的详细地图!
参考
- 概率与统计推断(第 9 版)
- 【https://www.statisticshowto.com/probability-and-statistics/
- https://www.onlinemathlearning.com/math-probability.html
如果你喜欢我的内容,请关注我❤️,看看我最近的博客:
作为数据分析师或数据科学家,我们不仅需要知道概率和统计,机器学习算法…
towardsdatascience.com](/how-to-prepare-for-business-case-interview-as-an-analyst-6e9d68ce2fd8) [## 构建电子商务产品推荐系统:第二部分——模型构建
这个博客是我之前工作的延续,在我之前的工作中,我谈到了我是如何收集产品评论和…
medium.com](https://medium.com/@kessiezhang/building-a-product-recommendation-system-for-e-commerce-part-ii-model-building-8b23a9b3ac27) [## 为电子商务建立一个产品推荐系统:第一部分——网络搜集
今天,如果我们想到机器学习在商业中最成功和最广泛的应用,推荐者…
medium.com](https://medium.com/@kessiezhang/building-a-product-recommendation-system-for-e-commerce-part-i-web-scraping-798b6251ab51) [## 如何将 Jupyter 笔记本转换成 PDF
用几行代码将 Jupyter 笔记本转换为 pdf(调试“500:内部服务器错误”)
towardsdatascience.com](/how-to-convert-jupyter-notebooks-into-pdf-5accaef3758)
在 TensorFlow 和 Keras 中理解和实现辍学
技术的
Dropout 是一种常见的正则化技术,在计算机视觉任务(如姿态估计、对象检测或语义分割)的最新解决方案中得到利用。
约翰·马特丘克在 Unsplash 上拍摄的照片
介绍
本文介绍了 dropout 技术的概念,这是一种在深度神经网络(如递归神经网络和卷积神经网络)中使用的技术。
丢弃技术包括在每个训练步骤中从神经网络中省略充当特征检测器的神经元。每个神经元的排除是随机确定的。
G.E Hinton 在 2012 年发表的论文中提出了这个简单的技术:“ 通过防止特征检测器的共同适应来改善神经网络。
在本文中,我们将深入揭示辍学的概念,并了解如何使用 TensorFlow 和 Keras 在神经网络中实施这一技术。
了解辍学技巧
神经网络在其输入和输出层之间有隐藏层,这些隐藏层中嵌入了神经元,神经元内的权重以及神经元之间的互连使神经网络系统能够模拟类似学习的过程。
使用https://playground.tensorflow.org/构建简单的神经网络
一般的想法是,神经网络架构中的神经元和层越多,其代表能力就越强。这种表示能力的提高意味着神经网络可以拟合更复杂的函数,并很好地概括训练数据。
简单地说,在神经网络层中,神经元之间的互连有更多的配置。
使用https://playground.tensorflow.org/构建的复杂神经网络
利用更深层次的神经网络的缺点是它们非常容易过度拟合。
过度拟合是一个常见的问题,它被定义为经过训练的机器学习模型无法很好地推广到看不见的数据,但同一模型在它接受训练的数据上表现良好。
退出的主要目的是最小化训练网络中过度拟合的影响。
Dropout 技术通过随机减少神经网络中互连神经元的数量来工作。在每一个训练步骤中,每个神经元都有可能被遗漏,或者更确切地说,从连接的神经元的整理贡献中被遗漏。
这种技术最小化了过度拟合,因为每个神经元变得独立地足够,在某种意义上,层内的神经元学习不基于其相邻神经元的合作的权重值。
因此,我们减少了对大量互连神经元的依赖,以从训练好的神经网络中产生像样的表示能力。
假设你训练了 7,000 个不同的神经网络架构,要选择最好的一个,你只需取所有 7,000 个训练过的神经网络的平均值。
辍学技术实际上模拟了这个场景。
如果神经元在训练步骤中被遗漏的概率被设置为 0.5;我们实际上是在每个训练步骤中训练各种不同的网络,因为在任何两个训练步骤中排除相同的神经元是非常不可能的。因此,利用退出技术训练的神经网络是在每个训练步骤中出现的所有不同神经元连接组合的平均值。
实际场景
在实际场景中,或者当测试利用了对看不见的数据的丢弃的已训练神经网络的性能时,需要考虑某些项目。
第一个事实是剔除技术实际上并没有在神经网络的每一层上实现;它通常在网络最后几层的神经元中被利用。
在已发表的论文中进行的实验中,有报道称,在 CIFAR-10 数据集上测试时,在最后一个隐藏层使用 dropout 时有 15.6%的错误率。这比在相同的卷积神经网络上测试相同的数据集时报告的 16.6%的错误率有所改善,但是在任何层中都没有包括丢弃技术。
第二点是,在实际场景中,当评估一个训练好的神经网络时,并不使用丢失。由于在评估或测试阶段没有使用丢弃,因此实现了神经网络的全部潜力。这意味着网络中的所有神经元都是活跃的,每个神经元的输入连接都比训练时多。
因此,期望将神经元的权重除以 1,减去丢失超参数值(训练期间使用的丢失率)。因此,如果训练期间的辍学率为 0.5,那么在测试时间内,每个神经元的权重结果减半。
实施辍学技术
使用 TensorFlow 和 Keras,我们配备了工具来实现一个神经网络,该网络通过在神经网络架构中包含脱落层来利用脱落技术。
我们只需要添加一行来在更广泛的神经网络体系结构中包括一个脱落层。Dropout 类有几个参数,但是现在,我们只关心“rate”参数。辍学率是一个超参数,表示在训练步骤中神经元激活被设置为零的可能性。rate 参数可以取 0 到 1 之间的值。
*keras.layers.Dropout(rate=0.2)*
从这一点开始,我们将逐步实现、训练和评估一个神经网络。
- 利用加载工具和库, Keras 和 TensorFlow
*import tensorflow as tf
from tensorflow import keras*
2.加载 FashionMNIST 数据集,归一化图像并将数据集划分为测试、训练和验证数据。
*(train_images, train_labels),(test_images, test_labels) = keras.datasets.fashion_mnist.load_data()
train_images = train_images / 255.0
test_images = test_images / 255.0
validation_images = train_images[:5000]
validation_labels = train_labels[:5000]*
3.使用 Keras 模型类 API 创建一个包含 dropout 层的自定义模型。
*class CustomModel(keras.Model):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.input_layer = keras.layers.Flatten(input_shape=(28,28))
self.hidden1 = keras.layers.Dense(200, activation='relu')
self.hidden2 = keras.layers.Dense(100, activation='relu')
self.hidden3 = keras.layers.Dense(60, activation='relu')
self.output_layer = keras.layers.Dense(10, activation='softmax')
self.dropout_layer = keras.layers.Dropout(rate=0.2)
def call(self, input, training=None):
input_layer = self.input_layer(input)
input_layer = self.dropout_layer(input_layer)
hidden1 = self.hidden1(input_layer)
hidden1 = self.dropout_layer(hidden1, training=training)
hidden2 = self.hidden2(hidden1)
hidden2 = self.dropout_layer(hidden2, training=training)
hidden3 = self.hidden3(hidden2)
hidden3 = self.dropout_layer(hidden3, training=training)
output_layer = self.output_layer(hidden3)
return output_layer*
4.加载实现的模型并初始化优化器和超参数。
*model = CustomModel()
sgd = keras.optimizers.SGD(lr=0.01)
model.compile(loss="sparse_categorical_crossentropy", optimizer=sgd, metrics=["accuracy"])*
5.训练总共 60 个时期的模型
*model.fit(train_images, train_labels, epochs=60, validation_data=(validation_images, validation_labels))*
6.在测试数据集上评估模型
*model.evaluate(test_images, test_labels)*
评估结果将类似于下面的评估结果示例:
*10000/10000 [==============================] - 0s 34us/sample - loss: 0.3230 - accuracy: 0.8812[0.32301584649085996, 0.8812]*
评估结果示例中显示的准确性对应于我们的模型的 88%的准确性。
通过一些微调和使用更重要的历元数进行训练,精确度可以提高几个百分点。
这里有一个 GitHub 存储库,用于存储本文中介绍的代码。
Dropout 是一种常见的正则化技术,在计算机视觉任务(如姿态估计、对象检测或语义分割)的最新解决方案中得到利用。该概念易于理解,并且通过将其包含在 PyTorch、TensorFlow 和 Keras 等许多标准机器/深度学习库中而更容易实现。
如果您对其他正则化技术以及它们是如何实现的感兴趣,请阅读下面的文章。
感谢阅读。
* [## 如何在 TensorFlow(Keras)中实现自定义正则化
了解如何使用 TensorFlow 和 Keras 相对轻松地实现自定义神经网络正则化技术。
towardsdatascience.com](/how-to-implement-custom-regularization-in-tensorflow-keras-4e77be082918) [## 神经网络中的批量标准化(代码)
通过 TensorFlow (Keras)实施
towardsdatascience.com](/batch-normalization-in-neural-networks-code-d7c9b88da9f5)*
理解和实现 LeNet-5 CNN 架构(深度学习)
技术和代码
在本文中,我们使用定制实现的 LeNet-5 神经网络架构在 MNIST 数据集上执行图像分类。
介绍
L eNet 是由 Yann LeCun 、 Leon Bottou 、 Yoshua Bengio 和 Patrick Haffner 在 1998 年的研究论文中提出的。该论文的许多列出的作者继续为深度学习领域提供了几项重要的学术贡献。
扬·勒昆莱昂·博图帕特里克·哈夫纳 ,以及 约华·本吉奥****
本文将介绍原始论文中描述的 LeNet-5 CNN 架构,以及使用 TensorFlow 2.0 实现该架构。
本文最后将利用实现的 LeNet-5 CNN 对来自 MNIST 数据集的图像进行分类。
这篇文章的内容:
- 了解卷积神经网络内的组件
- 深度学习和机器学习常用术语的关键定义
- 原研究论文 中提出的对 LeNet-5 的理解
- 用 TensorFlow 和 Keras 实现神经网络
本文中的内容是为各种水平的深度学习和机器学习学生编写的。
对于那些渴望获得编码的人,向下滚动到' LeNet-5 TensorFlow 实现'部分。
卷积神经网络
卷积神经网络是用于解决与图像相关联的任务的神经网络架构的标准形式。针对对象检测、人脸检测、姿态估计等任务的解决方案都有 CNN 架构变体。
CNN 架构的一些特征使得它们在一些计算机视觉任务中更有利。我以前写过深入研究每个特征的文章。
LeNet-5
LeNet-5 CNN 架构由 7 层组成。层组成包括 3 个卷积层、2 个子采样层和 2 个全连接层。
上图显示了 LeNet-5 架构的描述,如原始论文中所示。
第一层是输入层——这通常不被认为是网络的一层,因为在这一层什么也学不到。输入层的构建是为了接收 32x32,这些是传递到下一层的图像的尺寸。熟悉 MNIST 数据集的人会知道,MNIST 数据集图像的尺寸为 28x28。为了得到满足输入层要求的 MNIST 图像尺寸,对 28x28 图像进行了填充。
研究论文中使用的灰度图像的像素值从 0 到 255 归一化到-0.1 到 1.175 之间。归一化的原因是为了确保该批图像的平均值为 0,标准偏差为 1,这样做的好处是减少了训练时间。在下面的 LeNet-5 图像分类示例中,我们将归一化图像的像素值,使其取 0 到 1 之间的值。
LeNet-5 架构利用了两种重要的层结构:卷积层和子采样层。
在研究论文和下图中,卷积层用' Cx' 标识,子采样层用' Sx' 标识,其中' x' 是层在架构中的顺序位置。 Fx' 用于识别完全连接的层。这种层识别的方法可以在上图中看到。
官方的第一层卷积层 C1 产生 6 个特征图作为输出,并且具有 5x5 的核大小。内核/过滤器是窗口的名称,该窗口包含在权重值与输入值的卷积过程中使用的权重值。 5x5 也表示卷积层内每个单元或神经元的局部感受野大小。第一卷积层产生的六个特征图的尺寸是 28x28。
一个子采样层' S2' '跟在' C1' 层'之后。“S2”层将它从前一层接收的特征地图的维度减半;这就是通常所说的下采样。**
‘S2’层也产生 6 个特征图,每个对应于从前一层作为输入传递的特征图。此链接包含更多关于子采样层的信息。**
有关 LeNet-5 其余层的更多信息,请参见实现部分。
下表总结了每一层的主要特性:
LeNet-5 架构特性(按作者)
LeNet-5 张量流实现
我们从导入我们将使用的库开始实施:
- tensor flow:机器学习模型的实现、训练、部署的开源平台。**
- Keras:一个开源库,用于实现在 CPU 和 GPU 上同时运行的神经网络架构。**
- Numpy:一个用 n 维数组进行数值计算的库。**
**import tensorflow as tf
from tensorflow import keras
import numpy as np**
接下来,我们使用 Keras 库加载 MNIST 数据集。Keras 图书馆有一套易于使用的数据集。
我们还需要将数据集划分为测试、验证和训练。下面是每个分区类别的一些快速描述。
- 训练数据集 :这是我们用来直接训练神经网络的一组数据集。训练数据是指在训练期间暴露给神经网络的数据集分区。
- 验证数据集 :这组数据集在训练期间被用来评估网络在各种迭代中的性能。
- 测试数据集 :数据集的这个分区在训练阶段完成后评估我们网络的性能。
还需要将数据集中图像的像素强度从 0–255 到 0–1 的值范围进行归一化。
**(train_x, train_y), (test_x, test_y) = keras.datasets.mnist.load_data()
train_x = train_x / 255.0
test_x = test_x / 255.0train_x = tf.expand_dims(train_x, 3)
test_x = tf.expand_dims(test_x, 3)val_x = train_x[:5000]
val_y = train_y[:5000]**
在上面的代码片段中,我们扩展了训练和数据集的维度。我们这样做的原因是,在训练和评估阶段,网络期望图像成批呈现;额外的维度代表一批图像的数量。
下面的代码是我们实现实际的基于 LeNet-5 的神经网络的主要部分。
Keras 提供了实现分类模型所需的工具。Keras 提出了一种顺序 API,用于将神经网络的层堆叠在彼此之上。
**lenet_5_model = keras.models.Sequential([
keras.layers.Conv2D(6, kernel_size=5, strides=1, activation='tanh', input_shape=train_x[0].shape, padding='same'), #C1
keras.layers.AveragePooling2D(), #S2
keras.layers.Conv2D(16, kernel_size=5, strides=1, activation='tanh', padding='valid'), #C3
keras.layers.AveragePooling2D(), #S4
keras.layers.Conv2D(120, kernel_size=5, strides=1, activation='tanh', padding='valid'), #C5
keras.layers.Flatten(), #Flatten
keras.layers.Dense(84, activation='tanh'), #F6
keras.layers.Dense(10, activation='softmax') #Output layer
])**
我们首先将变量'lenet_5_model'
赋给 tf.keras.Sequential 类构造函数的一个实例。
在类构造函数中,我们接着定义模型中的层。
C1 层由线keras.layers.Conv2D(6, kernel_size=5, strides=1, activation='tanh', input_shape=train_x[0].shape, padding='same')
定义。我们使用 tf.keras.layers.Conv2D 类来构建网络中的卷积层。我们传递几个论点,在这里描述为。
- 激活函数 :将神经元的结果或信号转化为归一化输出的数学运算。激活函数是在网络中引入非线性的神经网络的组成部分。激活函数的引入使得神经网络具有更强的表达能力和解决复杂的函数。
其余的卷积层遵循与 C 1 相同的层定义,只是为参数输入了一些不同的值。
在介绍 LeNet-5 架构的原始论文中,使用了子采样层。在子采样层内,取落在 2x2 池窗口内的像素值的平均值,之后,该值乘以系数值。最终结果中会添加一个偏差,所有这些都是在值通过激活函数之前完成的。
但是在我们实现的 LeNet-5 神经网络中,我们使用了TF . keras . layers . average pooling 2d构造函数。我们不向构造函数传递任何参数,因为在调用构造函数时,必需参数的一些默认值已经初始化。请记住,网络中的池化图层的作用是在要素地图穿过网络时对其进行缩减采样。
网络中还有两种类型的图层,平坦图层和密集图层。
展平层是用类构造器TF . keras . layers . flatten创建的。
该层的目的是将其输入转换为一维数组,该数组可以输入到后续的密集层中。
密集层每层都有特定数量的单元或神经元,F6 有 84 个,而输出层有 10 个单元。
最后一个密集图层有十个单元,对应于 MNIST 数据集中的类的数量。输出层的激活函数是 softmax 激活函数。
- Softmax :激活函数,用于导出输入向量中一组数字的概率分布。softmax 激活函数的输出是一个向量,其中它的一组值表示一个类/事件发生的概率。向量中的值加起来都是 1。
现在我们可以编译和构建模型了。
**lenet_5_model.compile(optimizer=’adam’, loss=keras.losses.sparse_categorical_crossentropy, metrics=[‘accuracy’])**
Keras 通过我们之前实例化的模型对象提供了'编译'方法。compile 函数支持我们在幕后实现的模型的实际构建,该模型具有一些额外的特征,如损失函数、优化器和指标。**
为了训练网络,我们利用损失函数来计算网络提供的预测值和训练数据的实际值之间的差异。
伴随着优化算法( Adam )的损失值有助于对网络内的权重进行多次改变。支持因素,如动量和学习率时间表,通过使损失值尽可能接近零,提供了使网络训练收敛的理想环境。
在训练过程中,我们还将在每个时期后使用之前创建的评估数据集分区来验证我们的模型
**lenet_5_model.fit(train_x, train_y, epochs=5, validation_data=(val_x, val_y))**
训练之后,您会注意到您的模型达到了 90%以上的验证准确率。但是,为了更明确地验证模型在未知数据集上的性能,我们将在之前创建的测试数据集分区上评估经过训练的模型。
**lenet_5_model.evaluate(test_x, test_y)
>> [0.04592850968674757, 0.9859]**
在训练了我的模型之后,我能够在测试数据集上达到 98%的准确率,这对于这样一个简单的网络来说相当有用。
下面是本文代码的 GitHub 链接:
**** [## Richmond alake/tensor flow _ 2 _ 教程
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com。](https://github.com/RichmondAlake/tensorflow_2_tutorials/blob/master/13_lenet-5.ipynb)****
我希望这篇文章对你有用。
要联系我或找到更多类似本文的内容,请执行以下操作:
- 订阅我的 YouTube 频道 即将发布的视频内容 这里
- 跟着我上 中
- 在 LinkedIn 上连接并联系我
从头开始用 Java 理解和实现神经网络💻
学习最流行的概念💪有史以来的强类型语言。
虽然听起来有点吓人,但我们将从头开始用 JAVA 创建一个人工智能程序,我会解释所有的概念,你也可以边阅读边编码,建议编写程序而不是 Ctrl+C 和 Ctrl+V。
简单神经网络(图片由作者提供)
神经网络是一种以较小的方式代表人脑的计算系统。黄色的点叫做 " 神经元 ,连接它们的线叫做 " 突触 ,这些概念取自🧠.系统通过反向传播 的过程调整突触的【权值】来模拟大脑的学习过程。****
创建这个系统的第一步是制作我们自己的矩阵库,因为矩阵是神经网络的基本数据结构。
我已经创建了一个开源库,与本文中解释的概念相同,如果感兴趣,你可以贡献出来🙌
从头开始用 Java 理解和实现神经网络💻学习最流行的概念…
github.com](https://github.com/SuyashSonawane/JavaNet)
1.矩阵库
我们将创建一个矩阵类,并为其添加必要的功能,如矩阵的加法、减法、转置和乘法
class Matrix
{
double [][]data;
int rows,cols;
}
我们的矩阵类包含 3 个对象变量。数据是一个 2d 双数组来保存矩阵
2 的内容。保存矩阵行数的行
3。您可能已经注意到,这些列包含列!!
public Matrix(int rows,int cols)
{
data= new double[rows][cols];
this.rows=rows;
this.cols=cols;
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
data[i][j]=Math.random()*2-1;
}
}
}
这里,我们创建了一个构造函数,通过将行和列作为参数传递,用-1 和 1 之间的随机值初始化矩阵对象。
public void add(double scaler)
{
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]+=scaler;
}
}
}public void add(Matrix m)
{
if(cols!=m.cols || rows!=m.rows) {
System.out.println("Shape Mismatch");
return;
}
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]+=m.data[i][j];
}
}
}
这里,我们创建了一个“add”函数,它重载了两个参数,一个 double 和一个矩阵对象,该对象执行元素相加。
public static Matrix subtract(Matrix a, Matrix b) {
Matrix temp=new Matrix(a.rows,a.cols);
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
temp.data[i][j]=a.data[i][j]-b.data[i][j];
}
}
return temp;
}public static Matrix transpose(Matrix a) {
Matrix temp=new Matrix(a.cols,a.rows);
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
temp.data[j][i]=a.data[i][j];
}
}
return temp;
}
这是两个函数,用于计算矩阵的减法和转置,作为参数发送给类静态函数。这些函数返回新的矩阵对象。
public static Matrix multiply(Matrix a, Matrix b) {
Matrix temp=new Matrix(a.rows,b.cols);
for(int i=0;i<temp.rows;i++)
{
for(int j=0;j<temp.cols;j++)
{
double sum=0;
for(int k=0;k<a.cols;k++)
{
sum+=a.data[i][k]*b.data[k][j];
}
temp.data[i][j]=sum;
}
}
return temp;
}
public void multiply(Matrix a) {
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
this.data[i][j]*=a.data[i][j];
}
}
}
public void multiply(double a) {
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]*=a;
}
}
}
好吧!这里发生了很多事情🤯第一个乘法函数接受 2 个矩阵对象,并对各个矩阵执行点积运算,然后返回一个新的矩阵对象,第二个函数执行矩阵的逐元素乘法,最后一个函数通过缩放器对整个矩阵进行缩放或相乘。
public void sigmoid() {
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
this.data[i][j] = 1/(1+Math.exp(-this.data[i][j]));
}
}
public Matrix dsigmoid() {
Matrix temp=new Matrix(rows,cols);
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
temp.data[i][j] = this.data[i][j] * (1-this.data[i][j]);
}
return temp;
}
对于这个例子,我们为我们的人工智能🧠.使用了 Sigmoid 激活函数这两个函数将 sigmoid 和 sigmoid 的导数应用于矩阵的元素。计算反向传播的梯度时需要 Sigmoid 的导数。
public static Matrix fromArray(double[]x)
{
Matrix temp = new Matrix(x.length,1);
for(int i =0;i<x.length;i++)
temp.data[i][0]=x[i];
return temp;
}
public List<Double> toArray() {
List<Double> temp= new ArrayList<Double>() ;
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
temp.add(data[i][j]);
}
}
return temp;
}
这些是在矩阵对象和数组之间进行转换的辅助函数。
现在,✌,我们有了一个运行良好且易于调试的,我们自己的——矩阵库!!
2.构建神经网络
现在,神经网络的真正有趣和令人生畏的概念来了,但不要害怕,我们将通过编码来学习这些概念,你肯定会发现它们非常直观。
public class NeuralNetwork {
Matrix weights_ih , weights_ho , bias_h , bias_o;
double l_rate=0.01;
}
这里我们有一个包含许多变量的 NeuralNetwork 类,让我们逐个定义它们。
1。重量 _ih👉输入和隐藏层的权重矩阵。
2。权重 _ho👉隐藏层和输出层的权重矩阵。
3。bias_h👉隐藏层的偏差矩阵。
4。bias_o👉输出图层的偏差矩阵。
5。低利率👉学习率是一个超参数,用于控制权重优化过程中的学习步骤。
public NeuralNetwork(int i,int h,int o) {
weights_ih = new Matrix(h,i);
weights_ho = new Matrix(o,h);
bias_h= new Matrix(h,1);
bias_o= new Matrix(o,1);
}
这里,我们让构造函数在传递给构造函数的输入、隐藏和输出形状的帮助下初始化所有变量。
检查矩阵的形状很重要,因为点积仅适用于兼容矩阵。有关点积的更多信息,请遵循此
public List<Double> predict(double[] X)
{
Matrix input = Matrix.fromArray(X);
Matrix hidden = Matrix.multiply(weights_ih, input);
hidden.add(bias_h);
hidden.sigmoid();
Matrix output = Matrix.multiply(weights_ho,hidden);
output.add(bias_o);
output.sigmoid();
return output.toArray();
}
这里我们有预测函数,它在神经网络上进行前向传递或前向传播
前向传播是神经网络中的一个计算步骤,其中输入(矩阵)与隐藏层的权重(矩阵)相乘,然后偏差(列矩阵)与前一步骤的点积相加。最后,结果由激活函数按元素处理。这是在每一层上执行的,前一层的结果作为下一层的输入。
这一步也称为前向传递,用于从网络生成预测。
单个神经元的正向传递(图片由作者提供)
该函数接受输入的双数组,然后通过我们的帮助函数将它们转换为列矩阵。然后在两个层上计算向前传递,然后通过另一个帮助函数将输出展平到一个列表中。
public void train(double [] X,double [] Y)
{
Matrix input = Matrix.fromArray(X);
Matrix hidden = Matrix.multiply(weights_ih, input);
hidden.add(bias_h);
hidden.sigmoid();
Matrix output = Matrix.multiply(weights_ho,hidden);
output.add(bias_o);
output.sigmoid();
Matrix target = Matrix.fromArray(Y);
Matrix error = Matrix.subtract(target, output);
Matrix gradient = output.dsigmoid();
gradient.multiply(error);
gradient.multiply(l_rate);
Matrix hidden_T = Matrix.transpose(hidden);
Matrix who_delta = Matrix.multiply(gradient, hidden_T);
weights_ho.add(who_delta);
bias_o.add(gradient);
Matrix who_T = Matrix.transpose(weights_ho);
Matrix hidden_errors = Matrix.multiply(who_T, error);
Matrix h_gradient = hidden.dsigmoid();
h_gradient.multiply(hidden_errors);
h_gradient.multiply(l_rate);
Matrix i_T = Matrix.transpose(input);
Matrix wih_delta = Matrix.multiply(h_gradient, i_T);
weights_ih.add(wih_delta);
bias_h.add(h_gradient);
}
现在,在上面的训练函数中,我们将 X 和 Y 作为双数组,我们将它们转换为矩阵,从矩阵形式的目标 Y 中减去前向传递的输出。相减的结果是当前传递的样本的误差。该误差用于计算反向传播的梯度。sigmoid 函数的导数按元素应用于输出矩阵,输出矩阵返回梯度矩阵,然后乘以输出误差和决定学习步骤的学习速率。
反向传播正好与正向传递相反,在正向传递中,我们对权重矩阵进行转置,然后将它们乘以根据误差计算的梯度,这又返回用于调整当前层中权重的增量。使用梯度更新偏差。
神经网络中的反向传播(图片由sebastianraschka.com提供)
当反向支持从输出层运行到输入层时,我们对网络中的前几层重复相同的步骤。
最后,我们已经更新了当前样本的所有层的权重,这完成了数据集上的 1 个步骤。
public void fit(double[][]X,double[][]Y,int epochs)
{
for(int i=0;i<epochs;i++)
{
int sampleN = (int)(Math.random() * X.length );
this.train(X[sampleN], Y[sampleN]);
}
}
fit 函数接受 2 个 2d 数组,即 X 和 Y,以及历元数,即我们需要在数据集上迭代多少次。在这里,我们使用数据集中的随机数据点重复调用 train 函数,以便我们可以在数据集上概化网络。
这完成了我们的神经网络类🥳,现在我们可以在一个简单的数据集上测试我们的程序。
嗨,我是苏亚士·索纳瓦尼👋这是我的网络作品集
suyashsonawane.me](https://suyashsonawane.me/)
3.测试我们的程序
我们将利用 XOR 逻辑门的数据制作一个简单的 2d 双数组。
XOR 逻辑门(图片由作者提供)
static double [][] X= {
{0,0},
{1,0},
{0,1},
{1,1}
};
static double [][] Y= {
{0},{1},{1},{0}
};
现在,我们将创建一个神经网络对象,并在数据集上进行训练
NeuralNetwork nn = new NeuralNetwork(2,10,1);
nn.fit(X, Y, 50000);
我们使用 50000 个历元,因为数据集非常小,只包含 4 个样本。
跨时代的损失(作者图片)
double [][] input ={{0,0},{0,1},{1,0},{1,1}};
for(double d[]:input)
{
output = nn.predict(d);
System.out.println(output.toString());
}//Output
[0.09822298990353093]
[0.8757877124658147]
[0.8621529792837699]
[0.16860984858200806]
现在我们用双数组形式的输入来测试神经网络。输出看起来很棒,因为大于 0.5 的值类似于 1 输出,而另一个类似于 0 输出。
下面给出了所有的源代码**
结论
因此,我们已经成功地从零开始创建了一个神经网络,我们还创建了自己的矩阵库,可以无缝地处理所有的矩阵运算。我们还在一个小的 XOR 数据集上测试了它,得到了非常好的结果。
作为一名 Python 程序员,我对 Java 编程不是很熟悉,因为它需要太多的注意力在数据类型和类型转换上。😓分号;如果你发现了任何不必要或多余的代码,请在评论中分享,如果你有任何建议,请在下面评论。🤗
这就是所有的乡亲们
跟我来:
作品集:https://suyashsonawane.me/
推特:苏亚什·索纳万(@ SuyashSonawane)/推特
LinkedIn: 苏亚什·索纳万| LinkedIn
Github: 苏亚什·索纳万(Suyash Sonawane)
了解和预测新加坡转售 HDB 公寓价格
一个简短的案例研究,涉及数据收集,探索,可视化和机器学习,以了解在新加坡转售 HDB 单位价格
简介
超过 80%的新加坡居民居住在住房发展局(HDB)的公寓里,这些公寓是新加坡政府于 1960 年设计的,旨在解决城市综合住房的需求。由于新加坡土地稀缺,HDB 的公寓通常建在高层建筑中,以有效利用空间。对大多数新加坡人来说,结婚后就能拥有一套 HDB 公寓。HDB 公寓的一个独特之处是与之相关的 99 年租约:公寓的租约期限为 99 年,从公寓完工开始计算,到期后土地归还政府。这意味着在新加坡购买 HDB 公寓并不意味着拥有传统意义上的公寓,而是在剩余租约的特定期限内从政府手中租赁该公寓。
尽管如此,这并不意味着新加坡人必须在他们拥有的 HDB 公寓里度过余生。HDB 的公寓可以出售(尽管如果公寓是全新购买的话,要在 5 年后),这就是这个迷你项目的核心——HDB 公寓转售市场。作为一个年轻的成年人,也许很快就会想拥有一套 HDB 公寓,了解 HDB 转售公寓背后的价格并预测它们似乎是一个有趣的工作项目。
特征工程
原始数据集从 https://data.gov.sg/dataset/resale-flat-prices 的获得。该数据集提供了 2017 年至 2020 年 HDB 所有转售公寓交易的信息。
从 data.gov.sg 提取的原始数据的数据框架
该数据集中的显著特征如下:
i) 城镇:关联 HDB 公寓的城镇
ii) 公寓 _ 类型:关联 HDB 公寓的公寓类型。在新加坡,有 1 室公寓到 5 室公寓,还有比 5 室公寓稍大的行政公寓。(剩下的公寓类型:多代同堂的公寓,少之又少)。
iii) block :关联 HDB 公寓的 block
iv)street:关联 HDB 公寓的 street
v)storey _ range:关联 HDB 公寓的楼层范围。在该数据集中,给定的楼层范围在 3 的范围内(例如,10 到 12,这意味着公寓基于第 10 到 12 层)。
vi) 楼层 _ 面积 _ 平方米:关联 HDB 单位的建筑面积平方米
vii) 剩余 _ 租约:关联 HDB 单位的剩余租约年月
viii) 转售 _ 价格:关联 HDB 单位的转售价格
在假设可能影响 HDB 转售公寓价格的因素时,我想到了三个突出的因素。到最近的捷运/LRT 的距离——这是 HDB 公寓与铁路网连接程度的一个指标。
2。距离最近的购物中心的距离——新加坡的购物中心是一切(购物、食品、杂货、服务)的一站式目的地。
3。到最近的小学的距离 —附近有一所小学对父母来说是非常方便的,因为他们的孩子可以选择放学后步行回家,而不是选择其他交通安排。
捷运/轻轨、购物中心和小学的详细列表是从维基百科获得的。为了获得以下特征,使用 Onemaps API 获得了所有独特的 HDB 转售街区、MRT/lrt、购物中心和小学的纬度和经度。
For-loops 用于计算每个独特的 HDB 转售街区与所有 MRT/lrt、购物中心和小学之间的距离(使用哈弗森公式)。最近的捷运/LRT、购物中心和小学,以及它们到每个独特 HDB 街区的 HDB 公寓的相关距离(以 km 为单位)存储在数据帧中,如下所示。
显示每个独特的转售 HDB 区块及其相关的最近设施的数据框架。
需要注意的是,在 8836 个唯一 HDB 块中,405 个搜索地址返回了多个邮政编码差异很大的结果。结果,这 405 个地址被丢弃,因为它的纬度和经度无法从 Onemaps API 中准确识别。
接下来,变量 flat_type 、 storey_range 和 remaining_lease 被整数编码。此外,使用https://mrt.sg/fare,变量 time_to_raffles_place 通过从最近的 MRT/LRT 到 Raffles Place MRT 的时间创建,将其添加到从 HDB 到最近的 MRT/LRT 的步行时间(假设步行速度为 15 分钟/公里)。这一点意义重大,因为莱佛士广场地铁站是新加坡中央商务区的心脏,这里有许多新加坡劳动力的办公室。
最后,两个分类变量, MRT?和到期遗产?之所以被添加,是因为人们认为,与轻轨相比,更靠近轻轨的公寓可能存在价格溢价,到目前为止,其他列对此未作解释。同样,相对于非成熟屋而言,位于成熟屋的单位可能有溢价。
将 8431 个独特的 HDB 块与包含 77,895 个数据点的初始数据集合并。产生 NAs 的行(从先前删除的 405 行中)被删除,产生如下所示的最终数据集。
数据清理和特征工程后的最终数据集
最后,使用 describe()函数,可以对数据进行感觉检查,并找出特征工程中是否有错误。例如,到购物中心的最小距离输出为 0.000 公里。经过调查,发现出现这种情况是因为购物中心位于德惠里 137 号楼的正下方。
该图说明了为什么到购物中心的最小距离为 0.000 公里
经过几次类似的调查,发现工程特征的极限是令人满意的。
探索性数据分析和数据可视化
使用 describe()函数,观察到的汇总统计信息如下:
- 转售价格——公寓价格从 14 万美元(Toh Payoh)到 123.2 万美元(Pinnacle@Duxton)不等,平均价格为 44 万美元
- 楼层面积——公寓的面积从 31 平方米到 249 平方米不等,平均面积为 98 平方米
- 租赁——公寓的剩余租赁期从 45 年到 98 年不等,平均为 75 年
- 楼层-整数编码的楼层范围从 1 到 17(从 1 楼到 51 楼),平均值为 3(7 楼到 9 楼)
- 城镇——42%的转售公寓属于成熟地产;剩余的 58%属于未到期的遗产
- 最近的购物中心——从公寓到最近的购物中心的距离在 0.00 公里到 3.21 公里之间,平均值为 0.66 公里
- 最近的小学——从公寓到最近的小学的距离在 0.05 公里到 3.31 公里之间,平均为 0.40 公里
- 最近的地铁站/LRT——公寓到最近的火车站的距离在 0.002 公里到 3.50 公里之间,平均为 0.66 公里。此外,80%的转售公寓比 LRT 更靠近地铁站。
- 到莱佛士广场的旅行时间——从公寓到莱佛士广场的旅行时间在 11 分钟到 1 小时 36 分钟之间,平均 45 分钟
接下来,使用相关矩阵来理解每个预测因子之间的相关性。
预测值之间的相关矩阵
可以看出,转售价格的最大预测因素是楼层面积平方米,以及户型编码。还可以注意到,这两个相同的预测值具有 0.93 的高成对相关性,这是可以预期的,因为公寓类型越大(即房间数量越多),公寓的预期地板面积就越大。
转售价格相关条形图
观察相关性的符号,注意到符号与最初的假设一致:也就是说,与预期一致的是,距离预测值以及到莱佛士广场的时间与转售价格负相关(因为你离某些设施越远,公寓就越不值钱)。
还构建了地理价格热图,以了解整个新加坡转售公寓的位置和价格分布。
转售单位的地理价格热图
图右侧的色标有助于理解热图。可以观察到,黄色的平面倾向于位于中心区域附近。尽管如此,可以观察到东北部地区以及 Pasir Ris 的公寓比其他一些街区的颜色更浅。也许这是因为这些社区的 1-2 室公寓较少,价格最便宜。
箱线图也被用来了解公寓类型和转售价格之间的关系。
相对于转售价格的扁平型箱线图
可以看出,有相当多的异常值,尤其是在 3 室、4 室和 5 室公寓中。经过调查,发现其中一些 3 室公寓实际上是 6 室公寓,即两个相邻的 3 室公寓合并成一个新的单一单元。虽然 flat_type 仍然将这些公寓反映为三居室公寓,但 floor_area_sqm 反映了新合并单元增加的建筑面积。
据认为,其他预测因素可以帮助解释支付额外保费的原因,这些额外保费与从该箱线图中观察到的异常值有关。
利用机器学习预测 HDB 二手房的价格
设置训练&测试集
数据集有 74459 行。前 59649 行数据(~80%)用作训练集,其余 14810 行数据(~20%)用作测试集。train_test_split 不用于随机分配训练和测试数据,因为数据集遵循时间顺序,并且使用过去的数据来预测未来的数据更有意义。
包含在 x_train 中的特征是上述相关矩阵中的所有特征,不包括 flat_type_coded ,因为它与 floor_area_sqm 高度相关。
实施&评估机器学习模型 三种不同的机器学习回归模型被用于预测 HDB 公寓的转售价格:线性回归、决策树回归以及随机森林回归。
用于评估这些模型的三个标准是 r 平方(r2)、均方误差(MSE)以及均方对数误差(MSLE),结果如下所示。一篇非常有用的文章展示了 MSE 和 MSLE 之间的差异,可以在这里找到。
机器学习模型的评估标准
可以看出,随机森林回归模型表现最好,这是所有三个指标都同意的。它具有最高的 r2,以及最低的 MSE 和 MSLE 值。可以理解的是,由于它采用了使用决策树模型作为其基础模型的集成学习,因此预期它比决策树回归表现得更好。
下图显示了基于所选随机森林回归模型的要素重要性。可以看出,该模型定义的三个最重要的特征是其建筑面积、前往莱佛士广场的时间以及其剩余的租约。
基于随机森林回归模型的特征重要性
了解随机森林回归模型对城镇的影响 计算了 HDB 公寓实际转售价格与随机森林回归模型预测价格之间的差异。正的差异可能意味着公寓价格过高,而相反的差异可能意味着公寓价格过低(表明购买很划算)。
使用 groupby 和 pivot_table 函数,可以了解 town 和 flat_type 的预测值与实际值的平均差异。
实际值的平均差异-按城镇和平面类型预测
一室多代的大量 0 背后的原因是因为在测试集中没有这样的公寓可用于预测;这些公寓在 HDB 所有公寓中只占很小的比例。
通过观察,我们可以看到一些城镇,如 Kallang/Whompoa 和 Pasir Ris,在不同的公寓类型中显示出持续的负平均差异,这可能意味着这些城镇的房地产被低估了。
假设我现在正在新加坡寻找一套四居室公寓:下面的条形图描绘了新加坡各城镇四居室转售公寓的实际价格和预测价格之间的平均差异。
按城镇划分的 4 室转售 HDB 公寓的实际预测差异
从这个柱状图来看,Tampines、Pasir Ris、Jurong East 和 Ang Mo Kio 的 4 室公寓似乎很划算,我可能会考虑购买其他城镇的 4 室公寓。
局限性和结论
计算距离这可能不是最准确的函数,因为在某些情况下,到某个设施点的位移可能很短,但由于障碍物(例如中间的高速公路),到达该处的实际行程时间可能很长。这可以通过使用付费 API(例如 Google Maps API)来了解两个位置之间的行程时间,而不是简单地依赖位移来改善。
未说明的预测因子
虽然这个项目试图整合各种预测因子,但可以理解的是,它并不是一个详尽的列表。影响房价的不仅仅是这个项目中的预测因素。例如,转售公寓是走廊公寓(私密性较低)还是角落公寓(私密性较高)也会产生价格差异。靠近捷运/LRT,虽然是与公共交通可达性相关的一个因素,但不是公共交通可达性的决定性因素,因为公共汽车服务也存在,这在模型中没有说明。此外,随着新加坡的不断升级,价格也可能受到公寓附近未来发展的影响(例如,一条新的捷运线,或政府为提高房地产成熟度而进行的周边基础设施建设)。
结论
本文以一个问题陈述开始,并从特征工程着手。然后,它转移到数据探索和可视化。然后,机器学习被用来预测转售价格,以及它对寻求购买转售 HDB 公寓的新加坡人的影响。最后,本文以一些局限性结束。我希望这是一篇有益的阅读,它丰富了您对如何使用数据来解决问题的理解(这是我关于介质的第一篇文章)!
感谢李·任杰、李·肖辉和塞缪尔·蔡对本项目的投入和贡献。
理解和使用 k-最近邻(kNN)进行数字分类
什么是分类?
在机器学习和统计学中,分类是使用新观察的特征/变量来发现新观察的类别的任务。这种分类是使用基于训练数据训练的分类器来完成的。训练数据是许多观察值的集合,这些观察值用适当的类名正确标记。监督学习涉及用正确标记的观察值进行训练,因此分类被认为是监督学习。
分类示例:
- 使用电子邮件的文字、图像和附件等电子邮件特征将电子邮件标记为“垃圾邮件”或非垃圾邮件。
- 使用患者的特征,如性别、年龄、体重、血压和观察到的症状,将患者标记为“健康”或“患病”。
有许多模型可以用于机器学习中的分类任务,但我们将使用 k 近邻,因为它使用简单但功能强大。
k 近邻:
这是一种基于新数据点与其他数据点组的接近程度对其进行分类的算法。来自一个组的新数据点的接近度越高,它被分类到该组的可能性就越高。
数据点之间的距离通过距离度量来测量,如欧几里德距离、曼哈顿距离、闵可夫斯基距离、马哈拉诺比斯距离、切向距离、余弦距离等等。
对于具有 n 个特征的数据点 X 和 Y:
闵可夫斯基距离公式是:
闵可夫斯基距离当 p = 1 时是曼哈顿距离,当 p =2 时是欧几里德距离,当 p = ∞时是切比雪夫距离。闵可夫斯基距离是欧几里得距离的推广形式。
使用距离度量,我们创建新数据点的 n 个最近邻居的邻域。
为了获得新数据点的类别,我们查看在所创建的邻域中具有更多数据点的类别组,以及与邻域中的其他组相比更接近我们的新数据点的类别组。基于这两个因素,我们确定新数据点的类别。
让我们通过一个例子来更好地理解这一点。
举例:
考虑将顾客分为快乐和不快乐两类的任务。您可能希望事先知道哪些客户不满意,以便通过提供折扣来防止他们转向竞争对手的服务。
在图中,红色代表不满意的客户,他们已经转向另一家竞争对手,而蓝色代表对我们满意的客户,他们仍在使用我们的服务。
现在,我们有一个用绿色圆圈表示的新客户,我们想知道他对我们使用 kNN 算法的服务是满意还是不满意。
蓝色:快乐的顾客,红色:悲伤的顾客,绿色:新顾客
如果我们使用 3 个邻居,并且我们对每个数据点使用相等的权重,那么我们在邻居中有 2 个红色点和 1 个蓝色点,绿色点,即新的数据点被分类为红色。
具有统一权重的 3-最近邻示例
如果我们使用 5 个邻居,并且我们对每个数据点使用相等的权重,那么我们在邻居中有 3 个蓝点和 2 个红点,并且绿点被分类为蓝色。
具有统一权重的 3-最近邻示例
如果我们使用 5 个邻居,并且我们使用欧几里德距离来计算每个数据点的权重,那么我们在邻居中有 3 个蓝点和 2 个红点。数据点之间的欧几里德距离用线表示。
使用欧几里德距离度量的权重的 5-最近邻示例
为了使用欧几里得距离计算权重,我们将取距离的倒数,以便更近的点具有更高的权重。对于每个类,我们将计算权重的总和,具有较高总和权重的类成为预测类。
红色等级的权重总和:
1/3 + 1/4 = 0.5833
蓝色等级的权重总和:
1/5 + 1/8 + 1/6 = 0.4912
因为红色类别具有较高的权重,所以我们对新数据点的预测类别是红色类别。
使用 Scikit 的 kNN-learn:
kNN 超参数:
在机器学习中,在我们可以使用任何算法之前,我们需要为该模型选择超参数的值。在 kNN 的情况下,重要的超参数是:
- 邻居的数量。
weights
:如果设置为uniform
,每个邻域中的所有点对预测类的影响相等,即预测类是邻域中点数最高的类。如果设置为distance
,较近的邻居将比较远的邻居具有更大的影响,即具有更多靠近新数据点的点的类成为预测类,为此,我们在计算权重时取距离的倒数,以便较近的点具有更高的权重。metric
:当weights
设置为distance
时使用的距离度量。默认值为minkowski
,这是计算两个数据点之间距离的一种方法。我们可以更改默认值以使用其他距离度量。p
:是minkowski
度量的功率参数。如果 p=1,那么距离度量是manhattan_distance
。如果 p=2,那么距离度量是euclidean_distance
。如果愿意,我们可以试验更高的 p 值。
# kNN hyper-parametrs
sklearn.neighbors.KNeighborsClassifier(n_neighbors, weights, metric, p)
通过交叉验证尝试不同的超参数值可以帮助您为最终模型选择正确的超参数。
kNN 分类器:
我们将建立一个分类器,将手写数字从 0 到 9 分类。我们将使用的数据来自 MNIST 数据库,这是一组 60,000 个 28×28 像素的黑白图像,包含 0 到 9 之间的手写数字。
导入库:
# To load MNIST image data
from sklearn.datasets import load_digits# kNN Classifier
from sklearn.neighbors import KNeighborsClassifier# Confusion matrix to check model performance
from sklearn.metrics import confusion_matrix# To split data into training and testing set
from sklearn.model_selection import train_test_split# For plotting digit
import matplotlib.pyplot as plt
正在加载数字的 MNIST 数据:
digits = load_digits()
转换数据以用于 kNN 分类器:
# Number of images
n_samples = len(digits.images)# Changing shape from 28x28 pixel values to a sequence of values
X = digits.images.reshape((n_samples, -1))# Getting the already known targets for each image
y = digits.target
创建我们的培训和测试集:
# Splitting data to train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
创建和培训模型:
# Creating model
clf = KNeighborsClassifier(n_neighbors=3)# Training model
clf.fit(X_train, y_train)
获得测试数据的预测:
# Predictions for test data
predicted = clf.predict(X_test)
使用混淆矩阵比较实际目标值和预测目标值:
# Print confusion matrix
confusion_matrix(y_test, predicted)
分类数字混淆矩阵
在矩阵中,行代表实际目标值,其中第一行代表 0 标签,第二行代表 1 标签,依此类推。同样,列表示预测,其中第一列表示 0 标签,第二列表示 1 标签,依此类推。
以黄色突出显示的矩阵对角线上的值是预测正确的值。
考虑在第 4 列第 9 行用蓝色突出显示的值。这是一个错误。我们的模型将3 误分类为 8。
总的来说,我们的模型在将数字分类为误分类方面做得很好,即除对角线以外的值大多为零或小于 2。
查看前 10 幅图像和预测:
# Zip image with prediction
image_with_prediction = list(zip(digits.images, clf.predict(X)))# for first 10 images
for pos, (image, prediction) in enumerate(image_with_prediction[:10]):
plt.subplot(3, 4, pos+1) # Create 3x4 grid
plt.axis('off') # no axis
plt.imshow(image, cmap=plt.cm.gray_r) # show image in gray scale
plt.title("Prediction: %i" % prediction) # set title to predicted value
plt.show() # show plot
所有的预测看起来都不错,除了第 6 张看起来更像 5,但我们的模型认为它是 9。
结论:
当在你自己的问题上使用 kNN 时,确保每个特征的距离根据该特征的重要性进行缩放。如果出现房价问题,我们的特征年龄和房价将具有非常不同的比例,您必须缩小房价特征的比例以提高模型性能。
如果您的数据具有大量维度,则您可能希望通过使用要素缩减和要素工程技术来缩减要素,因为维度越高,数据的准确性越低。
kNN 在 MNIST 数据集上工作得很好,因为它是一个受控的数据集,即数字的位置在所有图像上都是一致的。此外,所有图像的像素值具有相似的颜色梯度。当处理具有大量空间信息的图像的分类问题时,即图像中物体位置的变化和颜色梯度的变化,您可能希望使用专门为此类任务构建的卷积神经网络。
kNN 也可用作回归算法,即,代替预测离散类,它也可用于预测连续数字,如房价。
希望这有助于你理解 kNN。
感谢阅读。
基于高斯混合模型的 Python 异常检测。
这里我们将通过一个例子来学习如何用高斯混合模型实现异常检测。
首先,我们需要理解数据集中什么算异常。异常可以被视为数据集中罕见或不寻常的观察结果。例如,在信用卡交易数据集中,欺诈交易是异常的,因为与大型数据集中的正常交易相比,欺诈案例的数量非常少。
在异常检测中,我们试图识别在统计上不同于其余观察的观察。
今天我们来看看高斯混合模型,这是一种无监督聚类方法。在这种方法中,与 K-Means 不同,我们对数据进行“K”高斯拟合。然后,我们找到高斯分布参数,如每个聚类的均值和方差以及聚类的权重。最后,对于每个数据点,我们计算属于每个聚类的概率。
在数学上,我们可以用如下两种方式编写高斯模型:
1]单变量情况:一维模型
单变量高斯模型
和
其中 μk =平均值&T2σk=第 k 个分量的方差。 ϕk =聚类‘k’的权重。
2]多元案例:多维模型
其中μk= mean&σk=第 k 个分量的协方差矩阵。 ϕk =聚类‘k’的权重。
总之,该方程描述了 K 高斯分布的加权平均值。
该算法在这 K 个集群上训练。因此,给定一个新的数据点,算法找到它与每个分布的距离&从而得到该点属于每个聚类的概率。因此,如果对于特定的聚类,如果概率非常低,则表明数据点是异常的。
所以让我们从实际实现开始吧。
首先,我们将导入所有必要的模块并创建一个数据集。
Import.py
这里我们使用来自 scikit-learn 的 make_blobs 来生成用于聚类的高斯 blob。可以在此处找到详细参数的完整文档:
[## sk learn . datasets . make _ blobs-sci kit-learn 0.22 文档
sci kit-learn:Python 中的机器学习
scikit-learn.org](http://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_blobs.html)
您得到的输出是一个数据集,其中有些点是主分布的异常值。
在上面的数据集中,我们可以用眼睛发现异常值,但我们应该如何让机器做同样的事情呢?
这就是高斯估计器的用处。由于我们的数据集只有一个中心,我们将使用单个高斯。如上所述,我们将使用第一个等式来计算数据集的均值和方差。为了做到这一点,我定义了函数,它返回输入分布的均值和方差。
均值 _ 方差. py
在上面的函数中输入我们的数据集后,
现在我们有了基本的参数,我们将计算每个点,由我们的分布产生的概率。因此,属于同一聚类的点将具有高概率&离群点将具有低概率。
为此,我们有一个函数。我用的代码是标准代码。这很容易理解。我们使用 Numpy 线性代数模块来实现上面提到的 N( x ∣ μi , σi ) 方程。
高斯. py
为了可视化这些概率,我们可以用它们的概率绘制相同的数据集,如下所示。
plot_prob.py
我们可以看到,同一个集群中的点有很高的概率&当我们向外走时,即离群值,概率趋于零。
现在,我们可以选择概率的阈值,将它们标记为异常值。这里我选择它为 0.02 &再次绘制数据。该值将根据分布和随机状态而变化。
mark_outliers.py
太好了!我们的模型在单个集群上表现得相当好。但是,我们可以对多个集群使用相同的策略吗?只有一个方法可以知道:)
这里,我使用了与上面生成单个集群相同的代码。唯一的区别是,这里不是 1 个集群,而是生成 5 个。
mixture_data.py
现在,让我们使用与之前相同的方法,看看它的性能如何。代码将是类似的。尝试不同的阈值并观察。这里我将使用一个固定值。
plot_mixture.py
我们可以看到,我们的模型在多个集群上表现糟糕。它没有认识到异常值。这就是为什么现在我们将转移到高斯混合。这里我们使用了sk learn . mixture . Gaussian mixture .你可以在这里看到文档。
[## sk learn . mixture . Gaussian mixture-sci kit-learn 0 . 22 . 1 文档
class sk learn . mixture . Gaussian mixture(n _ components = 1,协方差 _type='full ',tol=0.001,reg_covar=1e-06…
scikit-learn.org](https://scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html#sklearn.mixture.GaussianMixture)
高斯 _ 混合物. py
如果您遵循文档,您将看到 predict_proba() 方法。我们将使用相同的方法来找出每个点属于五个聚类中任何一个的概率。
混合物 _ 预测 _ 概率
我们可以看到,该模型将点分类到 K 个聚类 & 之一,这有助于我们识别离群点。我们可以通过找到任何集群都不希望自己拥有的点来做到这一点。
此公式返回数据点由我们适合的任何一个高斯随机产生的概率。因此,我们想要从上面的公式中过滤掉任何具有低概率的数据点。
如果你觉得这篇文章有用,请为它鼓掌,它会有很大的帮助!!
关注我的 中的 获取更多此类教程&文章。
如果您想联系,请在LinkedInT3 上添加我。
如果您有任何疑问或发现代码/概念中的任何错误,请随时在评论中写下。这是我的第一个博客,试图与世界分享我在数据科学领域所知道的一切!
了解 Apache 拼花地板
数据仓库|数据湖|拼花地板
理解为什么拼花地板应该用于仓库/湖边储物
Apache Parquet 是一种列存储格式,适用于任何项目[…],无论选择的是数据处理框架、数据模型还是编程语言。
——https://parquet.apache.org/
这个描述很好地总结了这种格式。这篇文章将讨论这种格式的特性,以及为什么它有利于数据仓库或 lake 中的分析数据查询。
数据一行接一行地存储,每行包含所有列/字段
https://www.ellicium.com/parquet-file-format-structure/
第一个特征是格式的列存储特性。这仅仅意味着数据是按列而不是按行编码和存储的。该模式允许分析查询为所有行选择列的子集。Parquet 将列存储为块,还可以在每个块中进一步拆分文件。这允许将磁盘 i/o 操作限制到最低限度。
要提到的第二个特性是数据模式和类型。Parquet 是一种二进制格式,允许编码数据类型。与某些格式不同,可以用特定类型的布尔、数字(int32、int64、int96、float、double)和字节数组来存储数据。这使得客户端在读取和写入 parquet 格式的数据时,可以轻松高效地序列化和反序列化数据。
除了数据类型之外,Parquet 规范还存储元数据,这些元数据在三个层次上记录模式;文件、块(列)和页眉。每个文件的页脚包含文件元数据。这记录了以下内容:
- 版本(拼花格式)
- 数据模式
- 列元数据(类型、值的数量、位置、编码)
- 行组数
- 附加键值对
拼花元数据https://parquet.apache.org/documentation/latest/
元数据总是写在文件的页脚,因为这允许单遍写入。简单地说,首先写入数据,然后知道写入数据的所有位置、大小和编码,就可以准确地写入元数据。许多格式将它们的元数据写在头中。但是,这需要多次传递,因为数据是在标题之后写入的。Parquet 使得读取元数据和数据本身非常有效。另一个优点是文件可以以任何期望的大小分割。例如,如果您将它与 Spark 或 Amazon Redshift 一起使用,您可以指定写入 1GB 大小的文件以提高加载效率。
单独的元数据文件是规范的一部分,允许引用多个拼花文件。因此数据集可以任意大小,以适合单个文件或多个文件。当在数据湖中处理千兆字节或兆兆字节的数据时,这尤其有利。今天的大多数应用程序和仓库都允许并行读取。多个文件意味着可以并行读取数据以加快执行速度。
将模式和元数据与可拆分文件相结合,使拼花成为一种灵活的格式。该模式可以随着时间的推移而发展。一个例子是,如果一个字段/列被添加到数据集,这只是在新的块和文件中编码。元数据文件被更新以记录只有某些文件和行组包括新块。因此,模式演化和合并很容易发生。如果文件不包含新字段,它们只会导致该字段不存在。如果读取多个文件,其中一些文件包含字段,而其他文件不包含字段,null
值用于表示缺失的列值。
最后,该格式本身支持文件内的压缩。这意味着当数据按列为中低基数时,可以有效地压缩数据。当数据基数较高时,可以在不同的文件中对这些列分别执行压缩。这允许不同字段和数据类型的编码和压缩的可变性。当以高吞吐量高效读取数据时,这是另一个优势。
总的来说,Parquet 以列格式存储数据以及模式和类型化数据的特性允许高效地用于分析目的。它通过压缩、编码和可拆分格式为并行和高吞吐量读取提供了更多好处。元数据支持所有这些,同时提供存储和模式演化的灵活性。Parquet 已经成为分析数据湖和数据仓库的事实上的格式。许多工具和框架都支持这一点,如 Hadoop、Spark、AWS Redshift 和 Databricks 平台。Databricks 还为 Parquet 提供了一种新的风格,允许数据版本化和使用 Delta Lake 格式进行“时间旅行”。(预告:可能会有即将发布的关于 Delta 格式的帖子。)
了解 ARIMA(时间序列建模)
杰克·希尔斯在 Unsplash 上的照片
利用过去来预测未来
那句老马克·吐温的名言是什么来着?"历史不会重演,但它经常会重复。"
我喜欢分析时间序列。所以当我说我认为马克·吐温是对的时,我可能是有偏见的。我们绝对应该以怀疑的态度对待所有的预测——未来本质上是不确定的,无论多少计算或数据都无法改变这一事实。但是通过观察和分析历史趋势,我们至少可以解开一小部分的不确定性。
ARIMA 模型是线性回归模型的一个子集,它试图使用目标变量的过去观察值来预测其未来值。ARIMA 模型的一个关键方面是,在其基本形式中,它们不考虑外生变量。更确切地说,预测纯粹是用目标变量的过去值(或从那些过去值精心制作的特征)做出的。
ARIMA 代表自回归综合移动平均线。让我们浏览一下 ARIMA 模型的每一部分,以便充分理解它。
AR:自回归
这是最简单的部分。自回归意味着我们对目标变量自身的过去值进行回归。也就是说,我们使用目标变量的滞后值作为 X 变量:
Y = B0 + B1*Y_lag1 + B2*Y_lag2 + ... + B***n****Y_lag***n***
这很简单。这个等式说的是 Y 的当前观察值是它过去的 n 值的某个线性函数(其中 n 是我们选择的一个参数;以及 B0、B1 等。是我们在训练模型时适合的回归 betas)。前面的等式通常被称为 AR( n )模型,其中 n 表示滞后的数量。通过稍微改变符号,我们可以很容易地预测未来:
Y_forward1 = B0 + B1*Y + B2*Y_lag1 + B3*Y_lag3 + ... B***n******Y_lag***(n-1)***
现在,我们使用当前值及其过去的滞后值来预测未来值(提前 1 个时间步)。
I:集成
集成表示我们对数据应用差分步骤。也就是说,不是像下面这样运行回归:
Y_forward1 = B0 + B1*Y + B2*Y_lag1 + ...
我们这样做:
Y_forward1 - Y = B0 + B1*(Y - Y_lag1) + B2*(Y_lag1 - Y_lag2) + ...
第二个方程的意思是,Y 的未来变化是 Y 的过去变化的线性函数,为什么要用差分呢?原因是差异通常比原始的无差异值更稳定。当我们做时间序列建模时,我们希望我们的 Y 变量是均值方差平稳。这意味着一个模型的主要统计特性不会因为取样时间的不同而改变。建立在静态数据上的模型通常更稳健。
以真实 GDP(真实意味着它已经被通货膨胀调整)为例。从图中可以明显看出,原始 GDP 数据不是稳定的。它的上升使得图表前半部分的平均 GDP 比后半部分的低很多。
美国实际国内生产总值(来源:圣路易斯美联储银行)
如果我们对这些数据求差,就会得到下面的曲线图。请注意,它现在明显更加稳定(多年来平均值和方差大致一致)。
实际国内生产总值的变化更加平稳
马:移动平均线
当我第一次研究时间序列时,我假设移动平均只是 Y 变量的移动平均(例如,200 天的移动平均)。尽管它们在精神上有些相似,但它们是不同的数学实体。
移动平均模型由以下等式总结:
Y = B0 + B1*E_lag1 + B2*E_lag2 + ... + B***n****E_lag***n***
类似于 AR 部分,我们在这里用历史值做一些事情,因此所有的滞后。但是这个 E 是什么?在对 MA 模型的大多数解释中,e 通常被称为误差,它表示模型和目标变量之间的随机剩余偏差(如果您问我们在拟合模型之前怎么可能会有误差,请稍等片刻)。
基本回归模型的完整等式是:
Y = B0 + B1*X + E
我们需要方程中的 E 来表明回归输出 B0 + B1*X 仅仅是 y 的近似值。左边的图说明了我的意思。黑点是我们试图预测的,蓝线是我们的预测。虽然我们成功地捕捉到了总体趋势,但总会有一些无法捕捉的特殊变化。E 项说明了这个未被捕获的部分——换句话说, E 代表精确答案和我们的模型提供的近似正确答案之间的差异。
因此,如果你一直在仔细阅读,你可能会问,“难道我们不需要先有一个模型,然后才会有模型错误吗?”你完全正确。e 是我们所说的不可观测的参数。与 Y(我们的目标变量)或外生 X 变量的滞后值不同,E 不可直接观察到。另一方面,这也意味着我们不能用 OLS 来拟合 ARIMA 模型(因为我们有不可观测的参数)。相反,我们需要像 MLE(最大似然估计)这样的迭代估计方法,它可以同时估计β参数和残差(以及残差项上的β)。
所以 MA 模型使用模型过去的误差来预测 Y(类似于 AR 模型,我们告诉它我们希望它考虑多少过去的误差)。
让我们花一点时间来思考一下为什么会这样。考虑以下简化的 MA(1)模型:
Y = u + B1*E_lag1where u = the mean of YE = Y - predicted
第一项,u,意味着我们的模型围绕 Y 的均值进行预测(就像说不知道别的,我就猜均值)。第二项 B1*E_lag1 是误差所在。让我们假设现在我们估计 B1 是 0.2。而误差(E)被定义为 Y 的实际值减去模型的预测值。
这意味着如果最近的误差(E_lag1)为正(意味着实际误差大于我们的预测),我们将把预测误差上调 1/5。这具有使模型的错误稍微减少的效果(因为第二项 B1*E_lag1 将模型的预测稍微推向正确答案)。
那么,如果模型知道自己的错误,那么为什么它不经常过度拟合(换句话说,为什么我们不通过不公平地给它一些答案来偏置我们的模型)?除了过度拟合,MA 模型没有固有的偏差,因为模型误差是独立的,并且近似正态分布(误差是一个随机变量)。因为它们是随机变量,误差(E_lag1)可以根据它们被观察的时间步长而呈现出非常不同的值。误差中的这种固有噪声意味着,仅用一个β(B1)来拟合,几乎不可能找到一个 B1 来完美地调整每个误差,以便他们的产品完美地填补实际和预测 Ys 之间的每个漏洞。换句话说,MA(1)模型只能利用它对误差的了解,非常近似地将自己推回到正确的方向。
当然,像任何其他模型一样,我们可以通过允许更多的特征来过度拟合 MA 模型,在这种情况下,将会有越来越多的滞后误差(这意味着拟合更多的 betas)。但是这样一个过度拟合的模型在样本之外会表现得很糟糕。
ARIMA 实际国内生产总值预测
让我们通过建立一个 ARIMA 模型来预测实际 GDP,从而将所有这些放在一起。我们将使用 ARIMA 的 statsmodel 实现。我从 FRED(圣路易斯美联储银行的数据仓库)中提取了真实 GDP 的数据。
statsmodel 中的 ARIMA 函数至少需要两个参数:
- 数据——在这种情况下,我们给它一个熊猫系列的原始实际 GDP 值(我们不需要提前求差,因为 ARIMA 算法会为我们做)。
- 第二个参数 order 告诉 ARIMA 函数,在下面的序列中要考虑每种模型类型的多少个组件— (AR 滞后,差值之间的时间步长,MA 滞后)。
让我们看看 AR(4)模型是什么样子的(没有 MA)。我们采用 1 个时间步长差值,使数据保持稳定:
from statsmodels.tsa.arima_model import ARIMAmod = ARIMA(master_df['GDP'], order=(4,1,0))
mod.fit()
predictions = mod.fit().predict()
我们的预测绘制在下图中。如果你仔细观察,橙色线(我们的预测)落后于蓝色线(实际)。这很糟糕。这意味着我们的预测总是落后于现实;因此,如果我们遵循这种模式,我们将永远落后几步。但这是 AR 模型的预期,AR 模型试图通过推断最近的过去来预测未来(如果有那么简单就好了)。
AR(4)模型
同样重要的是要注意,在这个演示中,我并没有将一个训练集和一个测试集分开;我使用整个数据集来拟合我的参数(而不是扩展窗口)。在实践中,我们希望对模型的样本外预测能力进行更加稳健的测试。因此,我们希望使用时间点数据(而不是修正后的 GDP 数据)和扩展窗口回归,以便在每个时间点,我们仅使用历史上该时间点的可用数据来估计参数。
现在,让我们将 2 MA 元件添加到模型中:
mod = ARIMA(master_df['GDP'], order=(4,1,2))
mod.fit()
predictions = mod.fit().predict()
我们已经通过允许模型考虑其误差的大小和方向,赋予了模型稍微修正航向的能力。这是以更长的模型估计过程为代价的——最大似然估计需要一些时间来收敛,而 OLS 则非常快。让我们看看我们的新预测是什么样的:
具有 4 个 AR 滞后和 2 个 MA 滞后的 ARIMA
也好不到哪里去。但是我们不应该期望仅仅通过增加几个 MA 组件就能获得巨大的改进。AR 和 MA 分量都来自目标变量的过去值,因此它们都是通过外推过去来预测未来的尝试。你可能会认为这在平静时期行得通,但在试图预测转折点时会遭遇惨败。
结论
因此,在经历了这一切之后,我们最终得到了一个乏善可陈的模型。但是不要绝望。ARIMA 模型并不意味着是一个完美的预测工具。相反,这是第一步。从我们的目标变量的过去值导出的特征意味着是外生变量的补充而不是替代。因此,在现实中,我们的 GDP 模型不仅包括 AR 和 MA 组件,还包括与 GDP 密切相关的外生组件,如通货膨胀、股票回报、利率等。
此外,拟合的测试版本身也常常令人感兴趣。例如,如果我们正在构建实际 GDP 的模拟,那么我们需要测量 GDP 的自相关性(自相关性是指 GDP 的当前变化与其过去值之间的相关性)。因为如果存在自相关,那么我们肯定不想建立一个 GDP 模型,在这个模型中,我们模拟每个季度的 GDP 变化,而不考虑其他因素。那将是错误的,我们的模型将产生脱离现实的结果。因此,分析 ARIMA 模型的贝塔系数有助于我们更好地理解感兴趣的目标变量的统计特性。
如果你总体上喜欢这篇文章和我的写作,请考虑通过我在这里的推荐链接注册 Medium 来支持我的写作。谢谢!
理解人工神经网络
检查深度学习模型和神经网络的基础
图片作者:特里斯特·约瑟夫
大约从 2013 年 11 月开始,“深度学习”一词开始流行起来,尤其是在数据科学界。这一趋势出现在 2010 年“大数据”热潮和 2011 年“数据科学”热潮之后不久。兴趣的上升并不令人惊讶,因为公司现在意识到,他们需要能够从目前的信息海啸中解读洞察力的个人。
随着数据科学现在被称为“几乎所有与数据有关的事物”,数据利用的过程已经超越了数据收集和分析。现在,可以使用大型数据集来精确地模拟事件和创建数据应用程序。这种激增使得训练计算机和机器执行各种任务成为可能,其中一些可能被视为今天的标准做法。
现在,当一项复杂的任务需要建模时,深度学习几乎是首选方法。这是一个强大的过程,通过自动提取未来决策所需的最有用的信息,使计算机能够模仿人类行为。它是人工智能(AI)和机器学习(ML)的一个分支,深度学习的目标是使用计算方法,允许机器直接从数据中理解信息,而不依赖于预先确定的方程作为模型。
图片作者:特里斯特·约瑟夫
ML 模型和深度学习模型都希望从数据中学习一些东西,这些数据可以用来帮助通知未来的决策。然而,深度学习模型比 ML 更进了一步。其思想是,典型的 ML 算法试图在数据中定义一组规则,这些规则通常是手工设计的。因此,当置于开发环境之外时,ML 模型可能表现不佳。另一方面,深度学习模型直接从原始数据中学习复杂的特征,不一定需要一套手工设计的规则。
深度学习模型建立在“神经网络”的思想之上,这就是允许模型从原始数据中学习的东西。回想一下,这类模型的目标是以某种方式让机器模仿人类行为。因此,想想类似于人脑的深度学习模型中的神经网络。大脑由数十亿个被称为神经元的细胞组成。每个神经元都有多个连接向它传递信息,一个连接向它传递信息。
随着这些神经连接的建立,大脑开始发育,人类通过一个叫做神经可塑性的过程进行学习。这是大脑中的神经网络通过生长和重组来改变的能力。当重组发生时,神经元之间的新路径得到发展,大脑可以删除不再必要的连接或加强发现更重要的连接。本质上,大脑会给每条路径分配一个权重,其中被认为更重要的连接会比那些被认为不重要的连接获得更高的权重。因此,一组输入沿着具有特定权重的路径被馈送到神经元,信息被处理,然后输出以执行一些任务。
图片作者:特里斯特·约瑟夫
类似地,人工神经网络(ANN)包含一组沿加权路径输入的输入,然后对这些输入进行处理,并产生一个输出来执行某些任务。与神经可塑性一样,如果发现神经网络中的路径在模型中更重要,则它们可以具有更高的权重。
这个想法有助于建立神经网络的基础;也就是感知器(或者单个神经元)。通过具有一组输入 x ,信息通过该系统向前传播,并且每个输入具有相应的权重 w 。输入还应包括独立于 x 的“偏置项”。给定手边的问题,偏差项用于相应地移动所使用的函数。然后将每个对应的输入和权重相乘,并计算乘积之和。然后,该和通过非线性激活函数,并且产生输出 y,。
非线性激活函数可以采取多种形式。然而,常见的一种是 sigmoid 函数。这是一条介于 0 和 1 之间的 S 形曲线,其中负输入值被指定为小于 0.5 的输出,正输入值被指定为大于 0.5 的输出。激活函数的选择很大程度上取决于手头的情况,尤其是因为不同的函数包含不同的属性。这个函数的重要性在于它的非线性。在“现实世界”的问题中,大部分数据是非线性的。将线性形式应用于非线性问题无疑会导致较差的性能。
图片作者:特里斯特·约瑟夫
现在我们了解了感知器是如何工作的,一个深度神经网络本质上是由多个连接的感知器组成的。如果所有输入都密集连接到所有输出,这些层被称为密集层。但是,与感知器不同,深度神经网络可以包含多个隐藏层。
隐藏层基本上是神经网络的输入和输出之间的点,在那里激活函数对输入的信息进行转换。它被称为隐藏层,因为它不能从系统的输入和输出直接观察到。只有一个隐含层的神经网络称为单层神经网络,而具有更多隐含层的神经网络称为深度神经网络。神经网络越深入,网络就越能从数据中识别。
必须注意的是,尽管从数据中学习尽可能多的东西是目标,但深度学习模型也可能会遭受过度拟合。当模型从训练数据中学习了太多内容(包括随机噪声)时,就会出现这种情况。然后,模型能够确定数据中非常复杂的模式,但这会对新数据的性能产生负面影响。训练数据中拾取的噪声不适用于新的或看不见的数据,并且该模型不能概括所发现的模式。像感知器一样,非线性在深度学习模型中非常重要。虽然模型将从多个隐藏层中学到很多,但是将线性形式应用于非线性问题仍然会导致较差的性能。
图片作者:特里斯特·约瑟夫
深度学习的重要性(以及 ANN 的延伸)植根于这样一种理念,即通过从数据中了解更多信息,可以做出更好的数据驱动决策。人类有能力通过识别旧的模式,发展新的联系,并以新的视角感知他们所学的东西来开发新的有效的过程,从而自发地将信息整合在一起。但是人类并不擅长处理高度复杂的情况,大多数“现实世界”的问题都是高度复杂的。如果计算机更像人脑,那不是很好吗?
参考文献:
digital trends . com/cool-tech/what-a-artificial-neural-network/
deepai . org/machine-learning-glossary-and-terms/hidden-layer-machine-learning #:~:text = In % 20 neural % 20 networks % 2C % 20a % 20 hidden,inputs % 20 enter % 20 into % 20 the % 20 network。
ncbi.nlm.nih.gov/pmc/articles/PMC4960264/
towards data science . com/introduction-to-artificial-neural-networks-ann-1 EAE 15775 ef9
explainthatstuff.com/introduction-to-neural-networks.html
neuralnetworksanddeeplearning.com/
其他有用的材料:
https://www.inertia7.com/tristn
youtube.com/watch?v=aircAruvnKk
youtube.com/watch?v=bfmFfD2RIcg
https://towards data science . com/what-is-deep-learning-ADF 5d 4 de 9 AFC
理解语音识别系统的音频数据、傅立叶变换、FFT 和频谱图特征
使用 python 进行音频数据分析(声音分析)的介绍
概观
几乎每个组织每天都会产生大量的音频数据。当数据科学家可以轻松访问音频数据以推动人工智能引擎和分析时,音频数据会产生大量的战略见解。已经意识到来自音频数据的信息的力量和重要性的组织正在利用 AI(人工智能)转录的对话来改善他们的员工培训、客户服务和增强整体客户体验。
另一方面,由于以下障碍,有些组织无法更好地利用他们的音频数据— 1。他们没有抓住它。2.数据质量很差。这些障碍会限制他们将要实施的机器学习解决方案(AI 引擎)的潜力。捕捉所有可能的数据并保证数据质量非常重要。
本文提供了从音频数据处理开始的分步指南。虽然这将有助于您开始基本分析,但在进入这一领域之前,对声波和基本信号处理技术有一个基本的了解也不失为一个好主意。你可以点击这里查看我关于声波的文章。那篇文章提供了对声波的基本理解,还解释了一些不同的音频编解码器。
在进一步讨论之前,我们先列出本文将要涉及的内容。让我们依次讨论以下每个主题—
- 读取音频文件
- 【傅立叶变换】
- 【快速傅立叶变换】
- 谱图
- 利用声谱图特征进行语音识别
- 结论
1.读取音频文件
利布罗萨
LibROSA是一个 python 库,它几乎拥有你在处理音频数据时需要的所有工具。这个丰富的库提供了大量不同的功能。这里有一个关于这些特征的快速展示—
- 加载并显示音频文件的特征。
- 光谱表示法
- 特征提取和操作
- 时间-频率转换
- 时间分割
- 顺序建模…等等
由于这个库很大,我们不打算讨论它的所有特性。为了便于理解,我们将只使用一些共同的特征。
这里是你如何快速安装这个库
***pypi** : pip install librosa
**conda** : conda install -c conda-forge librosa*
将音频加载到 Python
Librosa 支持大量的音频编解码器。虽然 。wav (无损)在涉及音频数据分析时应用广泛。一旦你成功地在你的 jupyter 笔记本上安装并导入了 libROSA。只需将 file_path 传递给librosa.load()
函数,就可以读取给定的音频文件。
**librosa.load()**
— >函数返回两个东西— 1。振幅数组。2.采样率。采样率是指录制音频文件时使用的【采样频率】。如果您保留参数**sr = None**
,它将以其原始采样率加载您的音频文件。(注:您可以根据自己的需求指定自定义采样率, libROSA 可以为您对信号进行上采样或下采样)。看下面这张图片—
sampling_rate = 16k 表示这段音频是以 16k 的采样频率录制(采样)的。换句话说,在记录这个文件时,我们每秒钟捕获 16000 个振幅。因此,如果我们想知道音频的持续时间*,我们可以简单地将样本数(振幅)除以采样率,如下所示*
"是的,你可以播放你笔记本里的音频."
IPython 为我们提供了一个通过笔记本播放音频文件的小部件。
可视化音频
我们从 librosa 得到了振幅和采样率。我们可以很容易地绘制出这些振幅随时间的变化曲线。 LibROSA 提供了如下所示的效用函数 waveplot()
**
这种可视化被称为给定信号的时域表示。这向我们展示了声波的响度(振幅)随时间的变化。这里振幅= 0 代表静音。(从声波的定义来看——这个振幅实际上是由于声音引起的大气压力变化而振荡的空气粒子的振幅)。
这些振幅不太能提供信息,因为它们只谈论录音的响度。为了更好地理解音频信号,有必要将其转换到频域。信号的频域表示告诉我们信号中存在哪些不同的频率。傅立叶变换是一个数学概念,可以将连续信号从时域转换到频域。让我们了解更多关于傅立叶变换的知识。
2.傅立叶变换
音频信号是由多个“单频声波”组成的复杂信号,这些声波在介质中作为扰动(压力变化)一起传播。当声音被记录时,我们只捕捉那些多重波的 合成振幅 。傅立叶变换是一个数学概念,可以 将信号分解成其组成频率 。傅立叶变换不仅给出信号中存在的频率,还给出信号中存在的每个频率的幅度。
傅立叶逆变换与傅立叶变换正好相反。它将给定信号的频域表示作为输入,并对原始信号进行数学合成。
让我们看看如何利用傅立叶变换将音频信号转换成其频率成分
3.快速傅立叶变换
快速傅立叶变换(FFT)是计算给定序列的离散傅立叶变换的数学算法。FT(傅立叶变换)和 FFT 的唯一区别是 FT 考虑连续信号,而 FFT 将离散信号作为输入。DFT 将一个序列(离散信号)转换成它的频率成分,就像 FT 对连续信号所做的一样。在我们的例子中,我们有一个从连续音频信号中采样的幅度序列。DFT 或 FFT 算法可以将这种时域离散信号转换到频域。
FFT 算法概述
简单正弦波理解 FFT
为了理解 FFT 的输出,让我们创建一个简单的正弦波。下面这段代码创建了一个正弦波,其 采样率= 100,振幅= 1,频率= 3 。每隔1/100 秒(采样率)* 计算一次振幅值,并存储到一个名为 y1 的列表中。我们将传递这些离散幅度值,使用 FFT 算法计算该信号的 DFT。*
如果您绘制这些离散值(y1 ),保持 x 轴上的样本数和 y 轴上的振幅值,它会生成一个很好的正弦波图,如下图所示——
现在我们有一个振幅序列存储在列表 y1 中。我们将把这个序列传递给由 scipy 实现的 FFT 算法。该算法返回在信号中找到的频率 的 复值振幅的列表 yf。该列表的前半部分返回正频率项,另一半返回与正频率项相似的负频率项。您可以选择任何一半,计算绝对值来表示信号中存在的频率。以下函数将样本作为输入,并绘制频率图—
在下图中,我们使用上述 fft_plot 函数绘制了正弦波的频率。可以看到,该图清楚地显示了正弦波中的单一频率值,即 3。此外,它还显示了与该频率相关的幅度,对于正弦波,该频率保持为 1。
为了检查一个信号的 FFT 输出,该信号具有超过一个频率的频率,让我们创建另一个正弦波。这次我们将保持 采样率= 100,幅度= 2,频率值= 11 。下面的代码生成这个信号,并绘制正弦波——
生成的正弦波如下图所示。如果我们提高采样率的话,会更平滑。我们保持 采样率= 100 ,因为稍后我们将把这个信号添加到我们的旧正弦波中。
显然,对于该波,FFT 函数将显示频率为 11 的单个尖峰。但我们想看看,如果将这两个采样速率相同但频率和幅度值不同的信号相加,会发生什么情况。这里序列 y3 将代表合成信号。
如果我们画出信号 y3,它看起来像这样—
如果我们将这个序列(y3)传递给我们的 fft_plot 函数。它为我们生成了下面的频率图。它显示了合成信号中两个频率的两个尖峰。所以一个频率的存在不会影响信号中的另一个频率。另外,需要注意的一点是,频率的 幅度与我们生成的正弦波的 一致。
对我们的音频信号进行 FFT
我们已经了解了 FFT 算法如何给出给定信号的所有频率。让我们尝试将原始音频信号传递到这个函数中。我们使用的是之前加载到 python 中的相同音频剪辑,采样率为 16000。
现在,看下面的频率图。这种【3 秒钟长】* 的信号是由数千个不同的频率组成的。频率值> 2000 的幅度非常小,因为这些频率中的大部分可能是由噪声引起的。我们绘制的频率范围是 0 到 8kHz,因为我们的信号是以 16k 采样率采样的,根据奈奎斯特采样定理,它应该只拥有≤ 8000Hz (16000/2)的频率。*
强频率范围从 0 到 1kHz 只是因为这个音频片段是人的语音。我们知道,在典型的人类语言中,这个频率范围占主导地位。
我们得到了频率,但是时间信息在哪里?
4.光谱图
为什么是声谱图
假设您正在处理一项语音识别任务。您有一个音频文件,其中有人在说一个短语(例如:你好吗)。你的识别系统应该能够以同样的顺序预测这三个单词(1。怎么样,2。是,3。你’)。如果您还记得,在之前的练习中,我们将信号分解为频率值,这些值将作为我们识别系统的特征。但是,当我们对信号应用 FFT 时,它只给出了频率值,我们失去了时间信息。现在,如果我们使用这些频率作为特征,我们的系统将无法判断先说的是什么。我们需要找到一种不同的方法来计算我们的系统的特征,这样它就有了频率值以及它们被观察的时间。这里 光谱图 进入画面。
给定信号的频率随时间变化的直观表示称为频谱图。在谱图表示图中,一个轴代表时间,第二个轴代表频率,颜色代表在特定时间观察到的频率的幅度(振幅)。下面的屏幕截图显示了我们之前讨论的同一音频信号的频谱图。明亮的颜色代表强频率。与之前的 FFT 图类似,较小的频率范围(0–1 khz)很强(明亮)。
创建和绘制光谱图
想法是将音频信号分成更小的帧(窗口)并计算每个窗口的 DFT(或 FFT)。这样,我们将获得每个窗口的频率,窗口号将代表时间。因为窗口 1 在前,窗口 2 在后…等等。保持这些窗口重叠是一个好习惯,否则我们可能会丢失一些频率。窗口大小取决于您正在解决的问题。
对于典型的语音识别任务,推荐使用 20 到 30 毫秒 长的 窗口。一个人不可能在这个时间窗口内说出一个以上的音素。所以保持窗口这么小,我们在分类时不会丢失任何音素。框架(窗口)重叠可以根据您的需要从 25%到 75%不等,通常为语音识别保留 50%。
在我们的谱图计算中,我们将保持窗口持续时间为 20 毫秒,窗口之间的重叠为 50%。因为我们的信号以 16k 频率采样,所以每个窗口将具有 (16000 * 20 * 0.001) = 320 振幅。对于 50%的重叠,我们需要前进 (320/2) = 160 个幅度值,以到达下一个窗口。因此,我们的步幅值是 160。
看看下图中的声谱图函数。在第 18 行中,我们制作了一个加权窗口(Hanning ) ,并在将其传递给第 20 行中的 FFT 函数之前,将其与振幅相乘。这里使用加权窗口来处理这个小信号(来自单帧的小信号)的不连续性,然后将其传递给 DFT 算法。要了解为什么需要加权窗口的更多信息— 点击此处 。
一个计算声谱图特征的 python 函数—
FFT 算法的输出是复数列表(size = window _ size/2),表示窗口内不同频率的幅度。对于大小为 320 的窗口,我们将得到一个包含 160 个振幅的频率仓的列表,在我们的例子中,这些频率仓代表从 0 Hz — 8kHz (因为我们的采样率是 16k)的频率。**
接下来,计算这些复数值幅度的绝对值并归一化。由此产生的 2D 矩阵就是你的声谱图。在这个矩阵中,行和列代表窗口帧号和频率仓,而值代表频率的强度。
5.利用声谱图特征的语音识别
我们现在知道如何生成频谱图,它是一个 2D 矩阵,表示给定信号的频率幅度和时间。现在把这个声谱图想象成一幅图像。您已经将音频文件转换为下图。
这就把它归结为一个 的图像分类问题 。这个图像适时地从左到右代表你所说的短语。或者把这个想象成一个图像,你的短语从左到右写,你需要做的就是识别那些隐藏的英文字符。
给定一个平行的英语文本语料库,我们可以训练一个深度学习模型,并建立一个我们自己的语音识别系统。这里有两个众所周知的开源数据集可以试用—
***Popular open source datasets —
1\. [LibriSpeech](http://www.openslr.org/12/) ASR corpus
2\. [Common Voice](https://voice.mozilla.org/en) [Massively-Multilingual Speech Corpus](https://arxiv.org/abs/1912.06670)***
深度学习架构的流行选择可以从以下优秀的研究论文中得到理解—
6.结论
这篇文章展示了如何从头开始处理音频数据和一些音频分析技术。此外,它还为构建语音识别系统提供了一个起点。尽管上述研究显示了识别系统非常有前途的结果,但是仍然有许多人不认为语音识别是一个已解决的问题,因为存在以下缺陷
- 研究人员提出的语音识别模型非常大(复杂),这使得它们难以训练和部署。
- 当多人在交谈时,这些系统不能很好地工作。
- 当音频质量不好时,这些系统不能很好地工作。
- 他们对说话者的口音非常敏感,因此需要针对每一种不同的口音进行训练。
这个研究领域有巨大的机会。可以从数据准备的角度(通过创建更好的特征)进行改进,也可以从模型架构的角度(通过呈现更健壮和可扩展的深度学习架构)进行改进。
原刊此处。
引用本文:谷歌学术链接
感谢您的阅读,请告诉我您的意见/反馈。
理解自动文本摘要-1:抽取方法
布雷特·乔丹在 Unsplash 上的照片
怎样才能自动汇总我们的文档?
一些网站和应用程序通常使用文本摘要来创建新闻提要和文章摘要。由于我们繁忙的日程安排,这对我们来说变得非常重要。我们更喜欢包含所有要点的简短摘要,而不是阅读整份报告并自己总结。因此,已经进行了几次尝试来自动化总结过程。在这篇文章中,我们将讨论其中的一些,看看它们是如何工作的。
什么是总结?
摘要是一种缩短长文本的技术,这样摘要就包含了实际文档的所有要点。
主要有四种类型的摘要:
- 单一文件摘要:单一文件的摘要
- 多文档汇总:来自多个文档的汇总
- 查询摘要:特定查询的摘要
- 信息摘要:它包括全部信息的摘要。
自动摘要的方法
主要有两种类型的总结:
基于抽取的摘要:抽取方法包括从文档中提取最重要的短语和行。然后,它将所有重要的行组合起来创建摘要。所以,在这种情况下,摘要的每一行每一个字实际上都属于被摘要的原始文档。
基于抽象的摘要:抽象方法包括基于深度学习的摘要。因此,它使用新的短语和术语,与实际的文件不同,保持要点不变,就像我们实际总结的方式一样。因此,这比提取方法要困难得多。
据观察,提取摘要有时比抽象摘要工作得更好,可能是因为提取摘要不需要自然语言生成和语义表示。
评估方法
有两种类型的评估:
- 人类评估
- 自动评估
人工评估:人工专家根据概要覆盖要点的程度、回答问题的程度以及语法性和非冗余性等其他因素来分配分数。
自动评估
ROUGE: ROUGE 代表面向回忆的替角,用于吉斯丁评价。它是通过将它与作为参考的由人类做出的其他摘要进行比较来确定摘要的质量的方法。为了评估该模型,有许多由人类创建的参考和由机器生成的候选摘要。这背后的直觉是,如果一个模型创建了一个好的摘要,那么它必须与人类参考有共同的重叠部分。它是由加州大学的林金耀提出的。
常见的 ROUGE 版本有:
ROUGE-n: 基于 n-grams 的机器生成输出与参考输出的比较度量。一个 n -gram 是来自给定文本或语音样本的 n 项的连续序列,也就是说,它只是一个单词序列。二元模型表示两个词,三元模型表示三个词,以此类推。我们通常使用二元模型。
”其中 p 是“候选与参考摘要之间共有 n 元文法的个数”,q 是“仅从参考摘要中提取的 n 元文法的个数”。- 来源
ROUGE-L: 它表示两个文本中最长的公共子序列越长,它们就越相似。因此,它比 n 元语法更灵活。它根据一个序列的长度来分配分数,这对于机器生成的候选人和人类参考来说是常见的。
胭脂素:它带来了一个概念跳过双字和单字。基本上,如果两个词之间有其他词,它允许或认为是一个二元词,即二元词不必是连续的词。
胭脂-2 最受欢迎,由以下机构提供:
其中对于每个二元模型‘I ’,我们计算它在生成文档 X 和参考文档 S 中出现的次数的最小值,对于所有参考文档,除以每个二元模型在所有参考文档中出现的总次数。它基于 BLEU 分数。
基于特征的摘要:由 IBM 的 H. P Luhan 于 1958 年开发。该论文提出句子的重要性是文档中高频词的函数。详细地说,该算法测量文档中单词和短语的频率,并考虑句子中的单词及其频率来决定句子的重要性。它指出,如果句子中有出现频率较高的单词,这是很重要的,但这里我们不包括像“a”、“the”这样的常见单词。等等。
抽取摘要:“ 抽取摘要技术通过选择原始文本中句子的子集来产生摘要”。- 来源
提取摘要器首先创建一个 中间表示 ,其主要任务是基于该表示突出显示或提取要摘要的文本的最重要信息。有两种主要的表现形式:
- 主题表征:侧重于表征文本中所表征的主题。有几种方法可以得到这种表示。我们在这里将谈论其中的两个。其他包括潜在语义分析和贝叶斯模型。如果你也想研究其他人,我会鼓励你查阅参考资料。
- 频率驱动方法:在这种方法中,我们给单词分配权重。如果这个词与主题相关,我们就给 1 分,否则就给 0 分。取决于实现方式,权重可以是连续的。主题表示的两种常用技术是:
- 单词概率:它只是简单的用单词出现的频率作为单词重要性的指标。单词 w 的概率由该单词出现的频率 f (w)除以总共有 N 个单词的输入中的所有单词给出。
对于使用单词概率的句子重要性,句子的重要性由句子中单词的平均重要性给出。
- TFIDF。(Tern Frequency Inver Document Frequency):这种方法是对单词概率法的一种改进。这里,TF-IDF 方法用于分配权重。TFIDF 是一种为在大多数文档中频繁出现的单词分配低权重的方法,直觉地认为这些单词是停用词或类似“the”的单词。否则,由于术语频率,如果一个单词以高频率唯一地出现在文档中,则它被赋予高权重。
- 主题词处理方式:这种方式类似鹿晗的方式。主题词技术是一种常见的主题表示方法,旨在识别描述输入文档主题的词。- 来源 这种方法计算单词频率,并使用频率阈值来找到可能描述主题的单词。它将一个句子的重要性分类为它包含的主题词数量的函数。
- 指示符表示:这种类型的表示依赖于句子的特征,并基于这些特征对它们进行排序。因此,在这里,句子的重要性不取决于它所包含的单词,正如我们在主题表征中看到的那样,而是直接取决于句子的特征。这种类型的表示有两种方法。让我们看看他们。
- 基于图形的方法:基于页面排名算法。它将文本文档表示为连通图。句子被表示为图的节点,节点或句子之间的边是两个句子之间相似性的度量。我们将在接下来的部分详细讨论这一点。
- 机器学习方法:机器学习方法将摘要问题作为分类问题来处理。这些模型试图根据句子的特征将句子分为摘要句和非摘要句。为了训练模型,我们有一组训练文档及其相应的人类参考文献摘要。这里通常使用朴素贝叶斯、决策树和支持向量机。
评分和选句
现在,一旦我们得到了中间表述,我们就开始给每个句子分配分数,以确定它们的重要性。对于主题表征,句子的得分取决于它所包含的主题词,而对于指示表征,得分取决于句子的特征。最后,具有最高分数的句子被挑选出来并用于生成摘要。
基于图形的方法
北德克萨斯大学的 Rada Mihalcea 和 Paul Tarau 的一篇论文首先介绍了基于图形的方法。该方法被称为文本排名算法,并受到谷歌的页面排名算法的影响。这个算法主要是试图找出一个给定图形中顶点的重要性。
现在,算法是如何工作的?
在这个算法中,我们已经了解到,每一个句子都被表示为一个顶点。连接两个顶点或两个句子的边表示这两个句子是相似的。如果任何两个句子的相似性大于特定阈值,则表示句子的节点由边连接。
当两个顶点连接在一起时,它描绘了一个顶点在给另一个顶点投票。特定节点(顶点或句子)的票数越多,该节点和所代表的句子就越重要。现在,投票也是有权重的,每一票的权重和重要性都不一样。投票的重要性还取决于投票的节点或句子的重要性,投票节点的重要性越高,投票的重要性就越高。所以,投给一个句子的票数和这些票数的重要性决定了这个句子的重要性。这是谷歌网页排名算法背后的相同想法,以及它如何决定和排名网页,只是节点代表网页。
如果我们有一个段落,我们会把它分解成一组句子。现在,假设我们将每个句子表示为顶点‘VI ’,那么,我们获得一组顶点 V。如所讨论的,边将一个顶点与同一组中的另一个顶点连接起来,所以边 E 可以表示为(V×V)的子集。在有向图的情况下,In(V{i})是节点的输入边的数量,Out(v{j})是给定节点的输出边的数量,顶点的重要性分数由 S{j}给出。
页面排序算法
根据谷歌页面排名算法,
其中 S(V{i})是所考虑的主题节点的分数,S(V(j))表示具有到 V{i}的外出边的所有节点。现在,V{j}的分数除以 V{j}的出度,这是对用户将选择该特定网页的概率的考虑。
细想一下,如果这是图,站在 A,作为一个用户,我可以同时去 B 和 C,那么我去 C 的几率就是,即 1/(A 的出度)。因子 d 称为阻尼因子。在最初的页面排名算法中,因子 d 包含了随机性。1-d 表示用户将移动到随机网页,而不是连接的网页。该因子通常设置为 0.85。在文本排序算法中实现了相同的算法。
现在,问题来了,我们如何获得分数?
让我们先检查页面排名算法,然后将其转换为文本排名。正如我们在上面看到的,有 4 个顶点,首先,我们给所有的顶点分配随机分数,比如,[0.8,0.9,0.9,0.9]。然后,概率分数被分配给边缘。
矩阵是图的邻接矩阵。可以观察到,相邻矩阵的值是概率值,即该节点或顶点的 1/出度。因此,实际上页面排名图变得不加权,因为等式只包含给出权重的项。
现在,整个等式变成了,
我们可以看到,旧的得分矩阵乘以邻接矩阵得到新的得分矩阵。我们将继续这一过程,直到新得分矩阵和旧得分矩阵的 L2 范数变得小于给定常数,通常为 1 x10^-8.这是一个基于线性代数和特征值与向量理论的收敛性质。为了简单起见,我们将跳过数学。一旦达到收敛,我们就从得分矩阵中获得最终的重要性得分。
对于文本排名算法,方程和图被修改为加权图,因为在这里,仅仅除以出度不会传达全部的重要性。结果,等式变成:
w 代表权重因子。
文本排序的实现包括两个不同的自然语言过程:
- 关键词提取任务,选择关键词和短语
- 一个句子提取任务,这确定了最重要的句子。
关键词提取任务
以前,这是使用频率因子完成的,结果相对较差。文本排名论文介绍了一个完全无监督的算法。根据该算法,对自然语言文本进行标记化和词性标注,并将单个单词作为节点添加到单词图中。现在,如果两个单词相似,相应的节点用一条边连接起来。使用单词的共现来测量相似性。如果两个单词出现在 N 个单词的窗口中,N 从 2 到 10 变化,则这两个单词被认为是相似的。具有最大数量的重要关联边的单词被选为最重要的关键词。
句子抽取任务
它的工作方式也类似于关键词提取,唯一的区别是在关键词提取中,节点代表关键词,这里它们代表整个句子。现在,为了形成句子排序的图,算法为文本中的每个句子创建一个顶点,并添加到图中。句子太长,所以不能应用共现度量。因此,本文利用两个句子之间的内容重叠来使用两个句子之间的“相似度”,换句话说,相似度取决于两个句子中出现的共同单词标记的数量。作者在这里提出了一个非常有趣的“推荐”见解。它们表示两个相似句子或顶点之间的边的连接,好像它在建议读者阅读另一行,这与他/她正在阅读的当前行相似。因此,我感觉的相似性表示两个句子之间的相似内容或兴趣。为了防止长句被推荐,重要性被乘以一个标准化因子。
两个句子之间的相似性由下式给出:
其中给定两个句子 Si 和 Sj,一个句子由出现在该句子中的 Ni 个单词的集合来表示:
最重要的句子是以与我们提取关键词相同的方式获得的。
这是一个关于文本排名如何运作的整体视图,请浏览原文以了解更多信息。
在实践中,对于摘要抽取,我们使用余弦相似度来决定两个句子之间的相似度。使用这种方法,我们可以得到几个连通的子图,这些子图表示整个文档中重要主题的数量。子图的连通部分给出了对相应主题重要的句子。
“ Pytextrank ”库允许直接在 python 上应用文本排名算法。
import spacy
import pytextrank
# example text
text = "Compatibility of systems of linear constraints over the set of natural numbers. Criteria of compatibility of a system of linear Diophantine equations, strict inequations, and nonstrict inequations are considered. Upper bounds for components of a minimal set of solutions and algorithms of construction of minimal generating sets of solutions for all types of systems are given. These criteria and the corresponding algorithms for constructing a minimal supporting set of solutions can be used in solving all the considered types systems and systems of mixed types."
# load a spaCy model, depending on language, scale, etc.
nlp = spacy.load("en_core_web_sm")
# add PyTextRank to the spaCy pipeline
tr = pytextrank.TextRank()
nlp.add_pipe(tr.PipelineComponent, name="textrank", last=True)
doc = nlp(text)
# examine the top-ranked phrases in the document
for p in doc._.phrases:
print("{:.4f} {:5d} {}".format(p.rank, p.count, p.text))
print(p.chunks)
由源码实现 Pytextrank 库。
有关应用详情,请参考 GitHub 链接。
结论
在本文中,我们已经看到了基本的摘要方法和 Textrank 算法的细节。对于抽象方法,请随意阅读本文的第 2 部分。
我希望这有所帮助。
参考
text rank:https://web . eecs . umich . edu/~ mihalcea/papers/mihalcea . em NLP 04 . pdf
概述:【https://arxiv.org/abs/1707.02268v3
基于机器学习的方法:http://www . csie . ntnu . edu . tw/~ g 96470318/A _ trainible _ document _ summarizer _。pdf
理解自动文本摘要-2:抽象方法
布雷特·乔丹在 Unsplash 上的照片
怎样才能用深度学习来概括课文?
这是我关于文本摘要的第二篇文章。在我的第一篇文章中,我谈到了总结文本的提取方法和使用的度量标准。在本文中,我们将讨论抽象总结。我们将看到深度学习如何用于总结文本。所以,让我们开始吧。
抽象总结者
抽象摘要器之所以被称为抽象摘要器,是因为它们不从最初给定的文本段落中选择句子来创建摘要。相反,他们使用不同于原始文档的词汇集,对给定文本的主要内容进行解释。概括地说,这与我们人类的行为非常相似。我们在大脑中创建文档的语义表示。然后,我们从我们的通用词汇表(我们常用的单词)中挑选符合语义的单词,以创建一个简短的摘要来表示实际文档的所有要点。正如你可能注意到的,开发这种摘要器可能很困难,因为他们需要自然语言生成。让我们看看最常用的解决问题的方法。
序列间 rnn 的应用
这个方法是由 IBM 的 Ramesh Nallapati、Bowen Zhou、Cicero dos Santos、卡格拉尔 Gulcehre 和 Bing Xiang 在一篇论文中提出的。使用术语“序列到序列模型”是因为模型被设计成从输入单词序列创建输出单词序列。在所考虑的情况下,输入序列是实际的文本文档,输出序列是缩短的摘要。
该文提出了一种受注意力递归神经网络编码器-解码器模型启发的模型,该模型由德国雅各布大学的 Dzmitry Bahdanau 首先提出用于机器翻译。
尽管如此,问题与你已经感觉到的有很大不同。首先,对于机器翻译,我们需要无损失的翻译,因为我们需要翻译形式的准确句子,但是对于摘要生成,我们需要压缩原始文档,以创建摘要,所以它需要有一点损失。其次,对于摘要生成,摘要的长度不取决于原始文本。这两点是问题给出的问题中的关键挑战。
在讨论本文的应用细节之前,我们先来看看编码器和解码器网络,以及使用注意力层的原因。
编码器和解码器网络
如果我们考虑一个一般的 LSTM(长期短期记忆)层,它看起来就像下面给出的图表。它或者为每个输入产生一个输出,或者它创建一个特征向量,该特征向量随后被密集神经网络层用于应用 softmax 层的分类任务。例如,情感检测,我们通过 RNN 传递整个句子,并使用适合 softmax 层的特征向量来产生最终结果。
但这里要意识到的一件事是,对于当前的问题,或者像这样的问题,包括机器翻译,更一般地说,我们可以说这个问题是一个序列到序列的问题,这种特殊的模型方法不能应用。主要原因是输出的大小与输入的大小无关,两者都是序列。为了解决这个问题,引入了编码器-解码器网络模型。
模型的基本架构如上图所示。
编码器负责接收输入句子或原始文档,并生成最终状态向量(隐藏状态和单元格状态)。这由图中的内部状态表示。编码器可能包含 LSTM 层、RNN 层或 GRU 层。由于消除了爆炸和消失梯度问题,大多数情况下使用 LSTM 层。
上图显示了我们的编码器网络。在编码器网络中,一个字在一个时间步长被馈送,最后,在第 n 个输入字被馈送到 LSTM 层之后,隐藏状态和单元状态成为我们的最终状态或特征向量。单元状态 Cn 和隐藏状态 Hn 被发送到解码器的第一组 LSTM 层。
这是我们的解码器模型的样子。现在,第一层接收来自编码器最终状态的输入,即隐藏和单元状态激活。解码器模型接收输入并产生输出序列的预测字,给定先前产生的字。因此,对于时间步长 1 的 LSTM,解码器具有 0 个向量输入,Y1 是生成的预测字,对于时间步长 2,Y1 作为输入被馈送到 LSTM 层,Y2 是生成的字,依此类推。解码器一步一步地生成单词,直到面对
这可能会提出一个问题,单词是如何生成的?。好吧,这就是答案。编码器-解码器模型是在单词的目标集合或词汇表上训练的。现在,在解码器的每个步骤中,LSTMs 隐藏激活通过 softmax 层发送,该层生成词汇表中每个单词被预测为下一个单词的概率。选择具有最大概率的单词作为该时间步长的输出。现在,模型如何知道哪个单词完全符合语义呢?为此,该模型在数据集上进行训练,并将问题转化为监督分类问题。此外,模型通常使用词汇中单词的单词嵌入,这些单词来自众所周知的嵌入向量,如 google 的 word2vec 或 Standford NLP 的 Glove。单词嵌入有助于获得关于单词的多种见解,例如给定的单词是否与给定的单词相似。有时 TFIDF 矢量化也用于生成上下文单词的意义。
我们来举个例子。假设我们有一个数据集,其中有一个长格式报告及其人工摘要的集合。这些信息可以用作训练我们的编码器-解码器网络的标签和目标。我们将矢量化我们的标签和目标,形成一个词汇表。接下来,我们将从 word2vec 或 Glove 中为我们的词汇表中的单词选取嵌入内容,然后将标签和目标与我们的训练模型相匹配。
但是这个特殊的总结问题有一个问题。原始文档可能非常大。假设文档有 100 行。现在,当我们人类从 1-5 行中总结时,我们需要考虑第 100 行吗?没有对吗?当我们手动总结时,我们会更多地关注第 1-5 行,然后慢慢向前。这一方面不能通过普通的编码器-解码器模型来实现,因此引入了注意机制。
注意机制
该机制旨在仅关注来自输入的一些特定序列,而不是整个输入序列来预测单词。这种方法与人类的方法非常相似,似乎可以解决问题。
上图是 TensorFlow 对注意力层的实现。我们可以看到,为了预测一个单词,输入序列中的每个单词都被赋予了一个权重,称为注意力权重。矢量化的注意力权重的总和用于形成用于预测的上下文向量。
让我们详细检查一下。该论文建议编码器由双向 GRU RNN 组成,解码器将具有单向 GRU RNN。编码器和解码器将具有相同数量的隐藏层和单元。
我们来看看这里的注意机制。
上面的绿色层显示解码器,Y1 和 Y2 是时间步长输出。X1,X2 是编码器的输入。“af0”是输入 0 的前向激活,“ab0”是时间步长 0 的后向激活,依此类推。
假设‘an’是(‘ABN’,‘afn’)在时间步长 n 的组合激活,那么,a0=(af0,ab0),即,向前和向后组合。“Wmn”确定在预测解码器在时间步长 n 的输出时,在时间步长 m 应该给予输入多少权重。
上下文向量是注意力权重的加权和。它由下式给出:
C < n > = Sum(W
)。一个 用于输入时间步长中的所有 m。
对于输入中的所有 m 个时间步,即,如果输入中有 x 个字,则 x 个时间步,上述等式表示时间步 n 的上下文向量等于在时间步 m 给予输入的权重和时间步 m 的组合激活的总和。
那么,单词是如何获得的呢?
W
=所有 m 的 softmax(e )
其中我们使用具有单一隐藏状态的神经网络来获得 e。网络接收 S(n-1)和 a(m)(时间步长 m 的组合激活)并给出 e(m,n)作为输出。用于获得‘e’的神经网络也在我们的编码器-解码器模型的训练期间被训练。
我们使用 softmax,因此分配给所有输入时间步长的所有权重之和始终等于 1。
这是注意力模型的整体机制。
大词汇绝招
我们之前已经讨论过,解码器预测的字是使用 softmax 生成的。现在,如果我们使用我们的词汇表中的所有单词作为我们的目标单词集,softmax 将有大量的输出节点,并且预测在计算上将是低效的。本文提出了蒙特利尔大学的塞巴斯蒂安·让提出的大词汇量的方法来解决这个问题。该模型在小批量中被训练。每个小批量的目标单词集或解码器词汇表被限制到该特定小批量的源文档。现在,如果我们对每个小批量使用不同的解码器词汇子集,目标词汇就有可能变得长度不等,这是非常明显的,因为不同的样本具有不同的行数和不同的字数。因此,我们将总词汇表中最常用的单词添加到子集词汇表中,使它们具有固定的大小。这降低了时间要求并加快了收敛,因为随着目标集大小的减小,softmax 层也缩短了。
提取关键词
之前,当我们讨论编码器-解码器模型时,我提到过我们通常使用单词嵌入来表示矢量化后的文档中的单词。现在,让我们试着想一想,为了进行总结,我们实际上需要这些词来表示什么。我们将意识到嵌入是不够的,因为对于摘要,我们需要关注文本片段中的上下文和关键词。嵌入有助于对一个词有一个大概的了解,但它与文本的上下文无关。因此,该论文提出考虑像词类标签、命名实体标签和单词的 TFIDF 统计以及嵌入来表示单词这样的因素。我们使用箱将连续的 TFIDF 值转换成分类值。最后,我们获取单词的所有特征和嵌入,并为单词创建新的嵌入。所以,基本上 TFIDF,POS 标签让我们了解单词在文档的上下文中有多重要,单词嵌入让我们了解单词的一般情况。接下来,我们将它们连接成一个长向量,并馈入网络。需要注意的一点是,我们只使用单词嵌入来表示目标端的单词。
接下来,我们将研究论文提出的另一个非常重要的方面。
切换发电机指针
在自然语言处理中,当我们使用监督模型训练模型时,我们经常需要处理一些我们的词汇表中不存在的单词。这样的词被称为 OOV 或词汇之外。在正常情况下,我们使用“UNK”标签来处理它们。但是在总结的情况下,这是不正确的,因为这些词在总结中可能具有某种意义。本文提出了一种开关解码器指针来处理这种情况。在解码器的每个时间步,都有一个指针指向输入文本。每当解码器面对 OOV 术语时,它指向输入中的术语,并直接使用输入文本中的术语。因此,解码器在一个时间步长内基本上有两个动作,它可以从目标字典生成一个单词,或者它可以指向并复制一个单词。这个决定是使用开关作出的,如果开关接通,它产生一个字,否则它从输入端复制一个字。
现在,问题是开关是如何操作的?该切换是在特定解码器时间步长下整个上下文向量上的 sigmoid 激活函数。它由下式给出:
来源于
“其中 P(si = 1)是开关在解码器的第 I 个时间步长打开的概率,hi 是隐藏状态,E[oi1]是来自前一个时间步长的发射的嵌入向量,ci 是注意力加权的上下文向量,Ws h、Ws e、Ws c、bs 和 vs 是开关参数”——来源
在每个时间步长的指针必须指向一个单词,以便复制该单词,这是基于该解码器时间戳的注意力权重分布来决定的。
“在上面的等式中,pi 是摘要中第 I 个字位置处的指针值,从文档字位置 j ∈ {1,.。。,Nd},其中 P a i (j)是解码器中第 I 个时间步长指向文件中第 j 个位置的概率,h d j 是编码器在 j 位置的隐藏状态“- 来源
在训练神经网络的过程中也训练开关。下面给出的函数在训练期间进行了优化。
“其中 y 和 x 分别为摘要词和文档词,gi 为指标函数”——来源 。每当 OOV 面对解码器词汇时,指示器功能被设置为 0。这将关闭开关,并从输入文本中复制一个单词。
这是用于文本摘要的序列到序列模型的概述。我鼓励您仔细阅读参考资料,了解更多实现细节。
需要注意的一点是,作者为长文档提出了层次化的注意层。如果文档非常非常长,我们有时可能需要用关键词来识别关键句子。为此,我们需要一个分层次的注意机制。一个层次负责句子的重要性,另一个层次负责单词的重要性。这两个注意层同时在两个层面上运作。
另外两种最著名的方法是:
脸书的模型
这种方法是由脸书人工智能研究所的 Alexander Rush 在 2015 年提出的。
上图描述了 facebook 的模式。它有三个编码器:
- 一个词袋编码器:它只使用输入句子的词袋表示,忽略与相邻词的关系。解码器接收编码向量或单词包,并在时间步长预测单词。
- 卷积编码器:卷积层用于从输入向量的字嵌入中生成特征向量,然后解码器用于在时间步长上创建字
- 基于注意力的编码器:这个编码器在注意力层 RNN 上工作,正如我们在前面的方法中所讨论的。
最后,对结果进行波束搜索,以获得摘要文本。
谷歌的指针生成器模型
这个模型是由斯坦福大学的 Abigii See 提出的。
我觉得这个模型类似于 IBM 的模型,但是这个模型使用了一个覆盖机制来减少序列到序列网络的重复问题。
你可以通过报纸了解更多的细节。我将提供参考中的链接。
结论
在本文中,我们讨论了深度学习用于文本摘要的几种方法。
我希望这有所帮助。
参考
IBM 的方法:https://arxiv.org/abs/1602.06023
谷歌的方法:https://arxiv.org/abs/1704.04368
https://arxiv.org/abs/1509.00685脸书的做法
神经机器翻译:https://arxiv.org/abs/1409.0473
大词汇绝招:https://arxiv.org/abs/1409.0473
关注层:https://www . tensor flow . org/tutorials/text/NMT _ with _ attention
了解 Azure Cosmos DB 中的自动驾驶模式
有了自动驾驶模式,计算您需要在 Cosmos DB 中提供多少吞吐量变得更加简单!
2019 年 11 月,Azure Cosmos DB 团队宣布了一系列很酷的功能,使得开发和管理 Cosmos DB 应用程序变得更加容易。
到目前为止,我最喜欢的功能是新的自动驾驶模式。在这篇文章中,我将谈论自动驾驶模式的好处,如何在启用自动驾驶模式的情况下供应数据库和容器,以及当前的限制是什么(毕竟它仍然是一个预览功能!😂)
开始时
在 Azure Cosmos DB 中,您提供吞吐量来管理您的工作负载。在 Autopilot 出现之前,您必须为一个容器或一个数据库可以拥有的请求单位/秒(RU/s) 设置一个硬性的最大限制。这是很难做到的,因为您必须猜测您需要多少吞吐量,并期待最好的结果。
即使你没有达到最高限额,你仍然被收取预订费。因此,您可以将最大吞吐量级别设置为 10K RU/s,但在一天中只使用 15 分钟。
我们可以使用一些工具来改变所调配的吞吐量。我们可以使用传送门(像穴居人一样)或者使用。NET SDK 以编程方式更新金额(这只是增加了代码的复杂性😥).
我试图计算出我需要调配多少吞吐量。来源:https://encrypted-tbn0.gstatic.com/images?q = tbn:and 9 gcrmf 4-sgz 3 rato qpcv 01 zreyvt 83 yj 99 _ tbnnuusbfmjw 4 _ kDX&s
激活自动驾驶模式!
进入自动驾驶模式😃我们可以在 Cosmos DB 中的容器和数据库上设置 Autopilot,而不是自己手动设置吞吐量,这将自动即时扩展所提供的吞吐量,而无需我们做任何事情,也不会影响我们应用程序的可用性。
Autopilot 目前在一个分层系统上工作,在这个系统中,您可以设置容器或数据库上不希望超过的最大吞吐量水平。容器或数据库可以在 0.1(最大值)到最大值之间瞬间伸缩。目前有 4 层可供选择:
- 4000 RU/s(最大存储容量 50GB)
- 20,000 RU/s (200GB 最大存储)
- 100,000 RU/s (1TB 最大存储)
- 500,000 RU/s (最大存储容量 5TB)
自动驾驶有什么好处?
首先,它更加灵活!我不能告诉你多少次,我不得不打破古老的算盘,计算出我不得不为宇宙(以及某些时候)提供的许多 RU。
这也使你的代码更加整洁。为了避开 429 错误。NET SDK 确实允许您在达到最大分配吞吐量时升级数据库或容器的吞吐量。但是这并不能避免我必须计算出我需要多少个 RU 来满足我的应用程序的需求。
自动驾驶也可以为你节省一些严重的现金!我曾经遇到过这样的情况,我们团队中的一名工程师在 Cosmos 中做了一些测试,将配置的 RU 提高到 20K。测试成功了,一切都很好,直到我们收到那个月的账单……
来源:https://I . kym-cdn . com/entries/icons/original/000/000/554/Picard-face palm . jpg
有了 Autopilot,你只需为你的 Cosmos DB 账户按小时使用的资源付费。因此,以 20K 为例,如果我们使用自动驾驶仪达到 RU 要求的水平,我们将只需支付我们需要 RU 水平达到 20K 的小时数,而不是整个星期,工程师都忘记将它降下来。
我什么时候会使用自动驾驶?
如果您的 Cosmos DB 帐户收到的工作负载是变化的或不可预测的,启用 Autopilot 可确保您有足够的吞吐量来满足工作负载的需求。这样就不需要自己更改调配的吞吐量,因为 Autopilot 会在一个层内为您增减吞吐量。
如果您正在开发一个新的应用程序,或者您只是在做一些测试,启用自动驾驶功能可以帮助您评估部署应用程序后需要提供多少吞吐量。
告诉我怎么做!
在自动驾驶的情况下创建数据库和容器是非常简单的。我将假设您知道如何提供一个 Cosmos DB 帐户,所以为了节省时间,我将跳过这一步。
让我们创建一个启用了自动驾驶的容器。如果你愿意,你也可以在数据库级别启用自动驾驶,但是我只用容器作为例子。在你的 Cosmos DB 账户中,点击 New Container。
就像我们在 Cosmos 中创建新容器时通常会做的那样,我们会给容器一个名称、分区键值,然后要么创建一个新数据库,要么给它分配一个预先存在的数据库。单击为容器调配专用吞吐量的选项,您应该会看到两个选项:
- 自动驾驶仪(预览)。
- 手动。
因为这是一个关于实现自动驾驶的教程,你可以继续选择那个选项😊
如果是这样,你必须为你的容器选择一个自动导航层。现在只需选择 4K 层,然后单击“确定”。
如果您稍后需要更改等级,请进入容器的'比例和设置'选项卡,您可以选择一个新的等级。您可以上升或下降一个级别,这取决于哪些需求会影响您的容器。
如您所见,设置起来相当简单😊
有什么蹊跷?
就目前的情况而言,当谈到自动驾驶时,你需要注意几个问题。希望当这个特性普遍可用时,这些问题会得到解决,但是如果你不想等那么久…..
你可以在 Cosmos 中的容器和数据库上启用自动驾驶的唯一方式是通过门户。如果您使用 CLI、Powershell 或 REST APIs 通过 CI/CD 管道部署这些,您目前将无法做到这一点。
我设法解决这个问题的一个方法是在门户中提供自动引导的容器,然后测试我的部署脚本,看它们是否恢复到手动提供的容器。好消息是自动驾驶功能仍然存在。这是一个有点黑客,但它的工作🤷♂️
您不能在现有的容器/数据库上启用自动驾驶😥就像如果你想改变一个容器的分区键,你必须重新创建这个容器。我真的希望当 Autopilot 正式发布时,我们可以更新这个(提示提示宇宙团队中可能读到这个的任何人😊)
你可以关闭自动驾驶,切换到手动,但是不能再回去了。再次,有点限制,但我非常希望当 Autopilot 正式上市时,我们将能够根据需要改变。
Autopilot 还会考虑你的容器/数据库当前存储了多少数据。例如,如果我们创建一个最大吞吐量水平为 4,000RU/s 的容器,我们的最大存储限制也将是 50GB。一旦我们在容器中超过 50GB 存储,我们将自动升级到 20,000 RU/s。如果您不需要在 Cosmos 中存储这些数据,并且希望降低成本,您可能需要考虑如何归档数据,以保持在该自动驾驶层的存储限制内。
结论……
希望在本文中,您看到了在您的 Cosmos DB 数据库和容器中使用 Autopilot 的好处。虽然这是到目前为止的结尾,因为它仍然是一个预览功能,所以当它变得普遍可用时,我很确定我将不得不再次写这篇文章!😂
在为 Cosmos DB 开发和管理应用程序时,我不能强调这是多大的游戏改变者。我们不再需要自己花时间摆弄吞吐量调配了!
一如既往,如果你有任何问题,请在下面的评论区提问!
了解轴和维度| Numpy |熊猫
知道如何沿数据的不同轴应用函数。
照片由 Ridham Nagralawala 在 Unsplash 上拍摄
我要解释一个非常基本但重要的话题,轴和尺寸。很多人觉得挺混乱的,尤其是在多维数据上应用一个函数的时候使用 axis。
轴或尺寸是一个非常通用的概念。无论是在 Numpy 、 Pandas、TensorFlow 、、还是另一个库中处理数据,都要经常碰到。我将要解释的概念在所有这些库中都是通用的。
轴心是什么?
简单来说,轴就是代表数据的维度的东西。让我们通过各种例子来理解它的核心。
零维数据
一个标量是零维数据。它没有维度或轴。
4
一维数据
一个向量是一维数据。Vector 是标量的集合。Vector 有一个形状(N,)
,其中 N 是其中标量的数量。
[1,2,3,4]
矢量只有一个轴,因为它是一维的。所以你只能跨axis-0
应用一个函数。轴的索引始终为 0。
np.sum([1,2,3,4], axis=0)>> 10
二维数据
矩阵是二维数据的一个例子。矩阵是向量的集合,形状为(N,M)
,其中N
是其中向量的数量,M
是每个向量中标量的数量。
以下示例矩阵的形状将是(2,3)
。
[[1,2,3],
[4,5,6]]
矩阵是二维数据,因此它有两个轴。让我们看看如何沿两个轴应用一个和函数。
应用求和函数
对axis-0
求和意味着我们对所有向量求和。
data = [[1,2,3],[4,5,6]]np.sum(data, axis=0)>> [5, 7, 9]
对axis-1
求和意味着,我们对一个向量中的所有标量求和。
data = [[1,2,3],[4,5,6]]np.sum(data, axis=1)>> [6, 15]
您也可以选择不在参数中提供任何轴。这样你将得到所有元素的总和。Means 函数适用于数据中存在的所有元素,与坐标轴无关。
data = [[1,2,3],[4,5,6]]np.sum(data)>> [21]
三维数据
类似地,3D 数据是 2D 数据点(矩阵)的集合。3D 数据的形状将是(N,M,P)
。会有形状为(M,P)
的N
矩阵。
下面的三维数据的形状将是(2,2,3)
。
[[[1,1,1],
[3,3,3]], [[2,2,2],
[4,4,4]]]
应用求和函数
- 对
axis-0
应用求和函数意味着你将所有矩阵求和。 - 对
axis-1
应用 sum 函数意味着您对每个指标内的所有向量求和。 - 对
axis-2
应用 sum 函数意味着对每个向量中的所有标量求和。
概括概念
让我们把这个概念推广到任意维数。维度为n
的数据将具有以下形状。
(N1, N2, N3 ..... Nn)
- 沿着
axis-0
有N1
个形状(N2, N3 .. Nn)
的数据点。对axis-0
应用函数意味着您正在这些N1
数据点之间执行计算。 - 沿着
axis-0
的每个数据点将具有形状(N3, N4 .. Nn)
的N2
数据点。这些N2
数据点将与axis-1
一起考虑。对axis-1
应用函数意味着您正在这些N2
数据点之间执行计算。 - 同样的,还在继续。
注意
您也可以对轴使用负索引。axis -1
将是最后一个轴,axis -2
将是倒数第二个轴。
多个数据点
我们在单个数据点内应用函数。让我们看看当我们在多个数据点之间应用一个函数时,轴是什么意思。
让我们看看下面的例子,我们在axis-0
上的两个不同的数据点上应用了 Sum 函数。
data1 = [1,2,3]
data2 = [4,5,6]np.sum((data1, data2), axis=0)>> [5, 7, 9]
由于有 2 个长度为 3 的向量,这些数据点将被视为形状为(2,3)
的单个数据点,如下所示。
[[1,2,3],[4,5,6]]
类似地,如果有N
不同的数据点,您可以将其视为一个组合的单个数据点,并像在单个数据点中应用一样应用任何函数。
熊猫的轴心
同样,在 Pandas 中,您可以将系列作为一维数据处理,将数据帧作为二维数据处理。
数据帧
例如,在数据帧中,行被认为是沿着axis-0
的,而列被认为是沿着axis-1
的。跨axis-0
应用任何函数意味着在所有行之间执行计算,跨axis-1
应用函数意味着在所有列之间执行计算。
在这篇博客中,我举了一个 Sum 函数的例子,但是您可以使用 axis 执行更多的函数。写这篇博客是为了给你的轴和维度打下基础。随着练习越来越多,你会越来越好。
下一个
与标准 Python 列表的比较。
towardsdatascience.com](/how-fast-numpy-really-is-e9111df44347)
了解神经网络的批处理规范化
了解批处理规范化如何使您的深度学习模型学习得更好,以及如何在 PyTorch 中从头实现它。
来源:链接
在训练学习系统时所做的主要假设之一是假设输入的分布在整个训练中保持不变。对于简单地将输入数据映射到一些适当的输出的线性模型,这个条件总是满足的,但是当处理由相互堆叠的若干层组成的神经网络时,情况并非如此。
在这种架构中,每一层的输入都受到前面所有层的参数的影响(网络参数的微小变化会随着网络变深而放大)。因此,在层内的反向传播步骤期间所做的小的改变可以产生另一层的输入的巨大变化,并且最终改变特征图的分布。在训练期间,每一层都需要不断地适应从前一层获得的新分布,这减慢了收敛。
批处理标准化[1]克服了这个问题,同时通过在训练期间减少内部层内的协方差移动(由于训练期间网络参数的变化而引起的网络激活分布的变化)以及使用批处理的优点,使训练更有效。
本文将涵盖以下内容
- 批处理规范化如何减少内部协方差偏移,以及这如何改进神经网络的训练。
- 如何在 PyTorch 中实现一个批处理规范化层?
- 一些简单的实验显示了使用批处理规范化的优点。
通过小批量统计减少内部协方差变化
一种减少或消除神经网络内部协方差偏移的不良影响的方法是标准化层输入。该操作不仅强制输入具有相同的分布,而且白化它们中的每一个。该方法受到一些研究2的启发,这些研究表明,如果网络训练的输入被白化,则网络训练收敛得更快,因此,加强每层输入的白化是网络的期望属性。
然而,每层输入的完全白化是昂贵的,并且不是完全可微分的。批处理规范化通过考虑两个假设克服了这个问题:
- 我们将单独归一化每个标量要素(通过设置平均值为 0 和方差为 1),而不是联合白化图层输入和输出中的要素。
- 我们没有使用整个数据集来标准化激活,而是使用小批量作为,每个小批量产生每次激活的平均值和方差的估计值。
对于具有 d 维输入的层,x = (x1,x2,..xd)我们通过以下公式获得归一化(通过批次 B 计算期望值和方差):
但是,简单地标准化图层的每个输入可能会改变图层所能表示的内容。例如,标准化 sigmoid 的输入会将它们限制在非线性的线性范围内。这种行为对于网络来说是不期望的,因为这将降低他的代表能力(这将变得等同于单层网络)。
sigmoid 函数图
为了解决这个问题,批量标准化还确保网络中插入的变换可以表示身份变换(模型仍然在每一层学习一些参数,这些参数在没有线性映射的情况下调整从前一层接收的激活)。这是通过引入一对可学习的参数 gamma_k 和 beta_k 来实现的,这两个参数根据模型学习的内容来缩放和移动归一化值。
最后,结果层的输入(基于先前层的输出 x)由下式给出:
批量标准化算法
训练期间
完全连接的层
全连接层的实现非常简单。我们只需要得到每一批的平均值和方差,然后用之前给出的 alpha 和 beta 参数来缩放和移动特征图。
在向后传递期间,我们将使用反向传播来更新这两个参数。
mean = torch.mean(X, axis=0)
variance = torch.mean((X-mean)**2, axis=0)
X_hat = (X-mean) * 1.0 /torch.sqrt(variance + eps)
out = gamma * X_hat + beta
卷积层
卷积层的实现几乎和以前一样。我们只需要执行一些整形,以适应我们从上一层获得的输入的结构。
N, C, H, W = X.shape
mean = torch.mean(X, axis = (0, 2, 3))
variance = torch.mean((X - mean.reshape((1, C, 1, 1))) ** 2, axis=(0, 2, 3))
X_hat = (X - mean.reshape((1, C, 1, 1))) * 1.0 / torch.sqrt(variance.reshape((1, C, 1, 1)) + eps)
out = gamma.reshape((1, C, 1, 1)) * X_hat + beta.reshape((1, C, 1, 1))
在 PyTorch 中,反向传播非常容易处理,这里重要的一点是指定我们的 alpha 和 beta 是在反向阶段更新它们的参数。
为此,我们将在我们的层中把它们声明为nn.Parameter()
,并用随机值初始化它们。
在推理过程中
在推断过程中,我们希望网络的输出仅取决于输入,因此我们不能考虑对我们之前考虑的批次进行的统计(它们与批次相关,因此它们根据数据而变化)。为了确保我们有一个固定的期望值和方差,我们需要使用整个数据集来计算这些值,而不是只考虑批次。然而,计算所有数据集的这些统计数据在时间和计算上都是非常昂贵的。
[1]中提出的方法是使用我们在训练期间计算的移动统计。我们使用参数β(动量)调整当前批次上计算的期望值的重要性:
这个移动平均值存储在一个全局变量中,该变量在训练阶段更新。
为了在训练期间在我们的层中存储这个移动平均值,我们可以使用缓冲区。当我们用 PyTorch 的方法register_buffer()
实例化我们的层时,我们初始化这些缓冲区。
最终模块
然后,最后一个模块由我们之前描述的所有模块组成。我们在输入数据的形状上添加一个条件,以了解我们正在处理的是全连接层还是卷积层。
这里需要注意的一件重要事情是,我们只需要实现forward()
方法。因为我们的类继承自nn.Module
,所以backward()
函数将自动继承自这个类(谢谢 PyTorch ❤️).
MNIST 实验
为了观察批规范化对训练的影响,我们可以比较没有批规范化的简单神经网络和具有批规范化的另一个神经网络之间的收敛速度。
为了简单起见,我们在 MNIST [3]数据集上训练这两个简单的全连接网络,而不进行预处理(仅应用数据归一化)。
无批量规范的网络架构
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.classifier = nn.Sequential(
nn.Linear(28 * 28, 64),
nn.ReLU(),
nn.Linear(64, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
def forward(self, x):
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
具有批处理规范的网络体系结构
class SimpleNetBN(nn.Module):
def __init__(self):
super(SimpleNetBN, self).__init__()
self.classifier = nn.Sequential(
nn.Linear(28 * 28, 64),
CustomBatchNorm(64),
nn.ReLU(),
nn.Linear(64, 128),
CustomBatchNorm(128),
nn.ReLU(),
nn.Linear(128, 10)
)
def forward(self, x):
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
结果
下图显示了我们的SimpleNet
第一层后获得的激活分布。我们可以看到,即使在 20 个时期的训练之后,具有批次范数的激活分布仍然是高斯分布(具有小的尺度和在训练期间学习到的移位)。
我们还可以看到收敛速度方面的巨大改进。绿色曲线(使用批处理规范化)表明,使用批处理规范化,我们可以更快地收敛到最佳解决方案。
结论
使用批量标准化进行训练的优势
- 小批量损失的梯度是对训练集梯度的估计,其质量随着批量的增加而提高。
- 由于 GPU 提供的并行性,批量规模的计算比单个示例的多次计算更有效。
- 在每一层使用批量标准化来减少内部协变量偏移,极大地提高了网络的学习效率。
参考
[1]约夫,谢尔盖和克里斯蒂安·塞格迪。"批量标准化:通过减少内部协变量转移加速深度网络训练."arXiv 预印本 arXiv:1502.03167 (2015)。
2下代拉,英敏。"通过加权对数似然函数改进协变量移位下的预测推断."统计规划与推断杂志90.2(2000):227–244。
[3]http://yann.lecun.com/exdb/mnist/ MNIST 数据集,
密码
我在神经网络上做的所有实验的集合——sinitame/神经网络——实验
github.com](https://github.com/sinitame/neural-networks-experiments/tree/master/batch_normalization)
理解 BERT —(变压器的双向编码器表示)
《变形金刚》第 2/3 部分 vs 谷歌 QUEST 问答标注(Kaggle top 5%)。
这是一个由 3 部分组成的系列,我们将经历变形金刚,伯特,和一个动手的 Kaggle 挑战— 谷歌任务 Q & A 标签 来看看变形金刚的行动(在排行榜上排名第 4.4%)。
在这部分(2/3)中,我们将关注 BERT(变形金刚的双向编码器表示)以及它如何成为各种现代自然语言处理任务的最新技术。
由于 BERT 的架构是基于变压器的,所以您可能想要检查变压器的内部结构,这可以在第 1/3 部分中找到。
我们意识到了这样一个事实,即在过去的几年里,迁移学习已经给计算机视觉领域带来了革命性的变化。像 VGG、YOLO、UNET、RESNET 等经过预训练的网络已经在计算机视觉的不同领域表现出了突破性的性能。
我们在自然语言处理领域也看到了类似的方法,如 Word2Vector、GloVe,但 BERT 将它提升到了一个全新的水平。
BERT 是谷歌人工智能语言的研究人员在 2018 年底发表的一篇论文,从那时起,它已经成为许多自然语言处理任务的艺术状态。BERT 采用不同的方法,它同时考虑输入句子的所有单词,然后使用注意机制来开发单词的上下文含义。
这种方法对许多 NLP 任务都很有效,正如最近 ELMo(来自语言模型的嵌入)论文中所示。
在这篇博客中,我们将通过 4 个分主题来讨论 BERT
- 伯特和以前的嵌入技术的区别。
- 引擎盖下看一看: 伯特内部架构。
- 前期训练的 BERT 是如何训练出来的: 让 BERT 如此高效的不同训练方法。
- 输入输出: 如何使用 BERT
BERT 与 Word2Vector 或 GloVe 等其他嵌入生成算法有何不同?
BERT 和 W2V 或 GloVe 的主要区别在于:
- W2V 和 GloVe 单词嵌入是上下文无关的。这些模型只为每个单词输出一个向量(嵌入),将单词的所有不同含义组合成一个向量。例如在给定的句子中:
“如果两个家伙***同时打成平手,这场比赛将导致一场平手。”
Word2Vector 或 GloVe 将无法捕捉到句子中的所有 3 个单词 tie 具有不同的含义,并将简单地为所有 3 个单词返回相同的嵌入。
而 BERT 是上下文相关的,这意味着 3 个单词中的每一个都将具有不同的嵌入,因为 BERT 在生成嵌入之前关注相邻的单词。*** - 因为 W2V 和 GloVe 是上下文无关的,所以我们不需要用于每次训练向量来生成嵌入的模型。我们可以简单地在单词语料库上训练向量一次,然后生成保存单词和它们各自的训练向量的表格或数据库。
而在 BERT 的情况下,由于它是依赖于上下文的,所以每次在生成嵌入或执行任何 NLP 任务时,我们都需要预训练的模型。
现在我们已经对 BERT 有了一些了解,让我们来理解它是如何工作的。
在引擎盖下看一看
BERT 的架构源自变压器。在 BERT 内部有几个堆叠的编码器单元,类似于我们在《变形金刚》中看到的。请记住,在转换器内部,编码器单元如何用于读取输入句子,解码器单元如何用于预测输出句子(逐字逐句),但在 BERT 的情况下,由于我们只需要一个读取输入句子并生成一些可用于各种 NLP 任务的特征的模型,因此只使用了转换器的编码器部分。
BERT 中的双向部分来自于它同时读取所有输入单词的事实。
来源:http://jalammar.github.io/illustrated-bert/
如我之前所说,编码器单元类似于我们在变压器中看到的。有自关注头然后是前馈神经网络。注意头和前馈神经网络也是定义不同类型 BERT 模型的参数。**
来源:http://jalammar.github.io/illustrated-bert/
如果我们看看两种伯特架构,即伯特基和伯特大,伯特基和伯特大都采用 512 维输入。
来源:http://jalammar.github.io/illustrated-bert/
- BERT base — 12 层(变压器块),12 个注意头,1.1 亿个参数,输出大小为 768 维。
- BERT Large — 24 层(变压器块),16 个注意头,3.4 亿个参数,输出大小为 1024 维。
来源:【https://medium.com/r/? URL = https % 3A % 2F % 2f towards data science . com % 2 funder standing-Bert-is-it-a-game-changer-in-NLP-7 CCA 943 cf 3 ad
在小队 v1.1 上,BERT 取得了 93.16%的 F1 分数,甚至超过了人类 91.2%的分数:BERT 还在极具挑战性的 GLUE 基准测试(一组 9 个不同的自然语言理解(NLU)任务)上提高了 7.6%的绝对水平。
*本文将编码器单元称为变压器模块。
训练模型
伯特是一个预先训练好的模型。它是在包括整个维基百科(25 亿字)和书籍语料库(8 亿字)在内的大型未标记文本语料库上进行预训练的。让我们来看看用来训练伯特的两种训练方法。
- 屏蔽语言模型(MLM): 在这种方法中,模型被输入一个句子,使得句子中 15%的单词被屏蔽。
BERT 面临的挑战是在给定非屏蔽词的上下文的情况下正确预测屏蔽词。
需要记住的几点是:
-最终编码器模块的输出不直接用于预测,而是在两者之间添加一个具有 GELU 激活的全连接层。然后,该层的输出被转换为 vocab,并应用 softmax 函数来预测屏蔽字。
-损失函数仅考虑屏蔽词的预测值,这使得学习更加基于上下文。
- 下一句预测(NSP): 在这种方法中,模型被输入两句话。伯特面临的挑战是预测两个句子的顺序。
比如,假设这两句话是:
*【我有一支笔】【笔是红色的】。在训练时,如果第一句在第二句之后,BERT 将返回 1,如果第二句在第一句之后,BERT 将返回 0。*
在训练 BERT 模型时,同时使用上面讨论的两种方法。
输入和输出
输入:
了解了 BERT 的架构和训练过程后,现在让我们了解如何在给定一些输入文本的情况下使用 BERT 生成输出。
特殊记号:在为 BERT 生成输入时,会用到一些特殊记号或关键字。主要有【CLS】【九月】。
【CLS】用作添加在输入句子开头的第一个标记。【SEP】**在传递多个输入句子时,用作不同句子之间的分隔符。
我们来考虑一个例子:假设我们要将两个句子“我有一支笔”和“这支笔是红色的”传递给 BERT。分词器会先把这些句子分词为:
['[CLS]','我','有',' a ','笔','[SEP]',' The ','笔',' is ',' red ','[SEP]'] 然后转换成数字记号。**
BERT 接受 3 种类型的输入:
- 记号嵌入 : 记号嵌入是输入句子中单词的数字表示。还有一种叫做子词标记化的东西,BERT 使用它首先将较大或复杂的单词分解为简单的单词,然后将它们转换为标记。例如,在上图中,在生成令牌嵌入之前,看看单词“playing”是如何被分解为“play”和“##ing”的。标记化中的这一调整创造了奇迹,因为它利用了一个复杂单词的子单词上下文,而不是像对待一个新单词一样对待它。
- 片段嵌入 : 片段嵌入用于帮助 BERT 区分单个输入中的不同句子。对于来自同一个句子的单词,这个嵌入向量的元素都是相同的,并且如果句子不同,该值也会改变。
我们来考虑一个例子:假设我们要将两个句子“我有一支笔”和“笔是红色的”传递给 BERT。分词器首先将这些句子分词为:
['[CLS]','我',' have ',' a ',' pen ','[SEP]',' is ',' red ','[SEP]'] 并且这些句子的段嵌入看起来像:
【0,0,0,0,0,0,1,1,1,1,1,1] 注意对应于第一个句子中的单词的所有元素如何具有 - 由于伯特采用 512 维输入,假设我们只有 10 个单词的输入。为了使标记化的单词与输入大小兼容,我们将在末尾添加大小为 512–10 = 502 的填充。连同填充符一起,我们将生成大小为 512 的屏蔽令牌,其中对应于相关单词的索引将具有 1 s,对应于填充符的索引将具有 0 s。
- 位置嵌入: 最后是在 BERT 内部生成的位置嵌入,它为输入数据提供了一种有序感。这和我们在《变形金刚》中讨论的是一样的。
输出:
请记住,对于输入中的每个单词,BERT 库在内部创建了一个 768 维的输出,但是对于分类这样的任务,我们实际上并不需要所有嵌入的输出。因此,默认情况下,BERT 只考虑对应于第一个令牌【CLS】的输出,并丢弃对应于所有其他令牌的输出向量。
这对于垃圾邮件检测之类的分类任务非常有用,对于给定的输入文本,我们需要预测它是否是垃圾邮件。
我们为输入句子生成 token _ embeddings、segmentation _ embeddings 和 mask_tokens,将它们传递给 BERT,它生成 768 维的输出。最后,我们将此输出传递到一个前馈网络,该网络类似于一个具有 2 个节点的密集层,并将 softmax 作为激活函数。
来源:http://jalammar.github.io/illustrated-bert/
上面输出的问题是,对于我们需要更多关注句子语义的任务,比如机器翻译,它并不那么有效。对于这样的任务,建议使用编码器隐藏状态的汇集或平均输出。这有时也被称为特征提取。
在对哪种向量作为情境化嵌入效果最好进行了大量实验后,论文提到了 6 种选择。
来源:http://jalammar.github.io/illustrated-bert/
事实证明,对于大多数任务,将最后四个编码器的隐藏状态串联起来似乎效果最好。
要了解更多关于输入参数和我的 BERT 返回的值,你可以查看这里的官方文档:https://huggingface.co/transformers/model_doc/bert.html
可用性
最后,让我们看看根据本文,BERT 可以执行哪些任务。
来源:http://jalammar.github.io/illustrated-bert/
(一)。对一对句子进行分类,例如一个问答对是否相关。
(二)。对单个句子进行分类,例如检测输入的句子是否是垃圾邮件。
(三)。生成给定问题标题和段落的答案。
(四)。单句标记任务,例如命名实体识别,必须为输入中的每个单词预测标记。
这就是伯特的博客,希望阅读愉快。
我要感谢所有的创作者,他们创作了我写这篇博客时提到的精彩内容。
参考链接:
- 应用人工智能教程:https://www.appliedaicourse.com/
- https://arxiv.org/abs/1810.04805
- https://towards data science . com/Bert-explained-state-of-art-state-language-model-for-NLP-F8 b 21 a9 b 6270
- http://jalammar.github.io/illustrated-bert/
- 【https://www.youtube.com/watch?v=BhlOGGzC0Q0】
- https://towards data science . com/understanding-Bert-is-it-a-game-changer-in-NLP-7 CCA 943 cf 3 ad
最终注释
感谢您阅读博客。我希望它对那些渴望做项目或学习 NLP 新概念的人有用。
在第 1/3 部分中,我们报道了变形金刚如何成为各种现代自然语言处理任务及其工作的艺术。
在第 3/3 部分中,我们将经历一次动手的 Kaggle 挑战——Google QUEST Q&A Labeling以观看变形金刚的行动(在排行榜上排名第 4.4%)。
在 LinkedIn 上找到我:www.linkedin.com/in/sarthak-vajpayee
和平!☮
理解偏差——方差权衡:示例和简单解释
本教程涵盖了你需要知道的关于偏差和方差的一切
偏差和方差是一些很容易理解但很难掌握的主题。因此,在本教程中,我将解释偏差和方差,这样你就再也不用担心它了。不浪费太多时间,让我们开门见山吧。
偏差 101:
偏差与训练集误差有关,也与欠拟合有关。
让我们从上面关于偏见的陈述中理解我的意思。
想象你有一个分类器,任何分类器,你在训练集上训练分类器,然后计算训练集误差;然后,如果训练集误差高,我们可以说我们的模型具有高偏差。并且如果训练集误差低,那么我们可以说我们的模型具有低偏差。
简而言之,高训练集误差对应于高偏差。高训练集误差背后的主要原因是我们的模型对训练数据拟合不足。这就是为什么高偏差也与欠拟合有关。
差异 101:
方差与测试集误差(与训练集误差相比,测试集误差有多大)相关,也与过拟合相关。
让我们再一次理解我上面关于方差的陈述是什么意思。
在训练分类器并获得训练集误差之后,我们必须获得测试集误差,并将训练集误差和测试集误差相互比较。
如果训练集和测试集误差之间的差异更大(测试集误差比训练集误差高得多),那么我们可以说我们具有高方差。获得 0.5%的训练集误差和 10%的测试集误差意味着我们的模型不能很好地概括,这意味着我们的模型也是过度拟合的。
简而言之,当测试集误差比训练集误差大得多时,我们有高方差。当这种情况发生时,我们的模型也是过度拟合的。
偏差-方差权衡 101:
偏差-方差权衡之所以出现是因为,
减少偏差会增加方差&减少方差会增加偏差。
简而言之,如果你试图减少太多的偏差,那么这将导致模型更加复杂和不通用。意味着你会过度适应模型。
如果你试图减少太多的方差,那么这将导致一个更一般化和更简单的模型。也就是说你会低估这个模型。
因此,我们的目标是在偏差和方差之间找到正确的平衡。
可视化不同的偏差-方差情景:
现在,为了更好地理解,让我们在一个包含两个维度(x1 和 x2)和两个标签(o 和 x)的数据集上可视化不同的偏差-方差场景。对于所有不同的偏差-方差情况,我们讨论了不同的决策边界。
这个图有四种不同的偏差-方差情况,你可以在其中找到自己。为了更好地理解,让我们把它们形象化。
Low Bias & High Variance:
如果我们的训练集误差较低(0.5%),而测试集误差较高(10%)。然后,我们有低偏差和高方差条件,这也被称为模型的过拟合。低偏差和高方差的例子可以在二维双标签数据集中看到。
High Bias & Low Variance:
如果我们有一个高的训练集误差(13%),而测试集误差(13.50%)与训练集误差相比并不高,那么我们就有高偏差和低方差的情况。也称为欠拟合。上面可以看到在二维& 2 标签数据集上的例子。
High Bias & High Variance: ***The worst case scenario.***
如果我们有高训练集误差(12%)和高测试集误差(20%),那么我们有高偏差和高方差条件。这是下装和上装的结合。其示例可以在上面的图像中看到,上面显示了二维 2 标签数据集。
Low Bias & Low Variance: ***This is what we want***.
如果你有低训练集误差(0.5%)和低测试集误差(1%),那么我们可以说我们有低偏差和低方差的完美模型。其例子可以在上面看到。
解决高偏差和高方差问题:
如果你有一个高偏差或高方差的问题,那么尝试上述解决方案将会奏效。但是要小心!当你试图减少偏差时,注意方差,因为你不想让偏差太低,因为那会增加方差。这一切都是为了找到偏差和差异的最佳点。
结论:
所以,你将不得不处理偏差-方差权衡,现在你明白了什么是偏差-方差权衡,以及如何解决偏差-方差问题。我想你会更轻松地处理这些事情。
我希望你现在没有任何疑虑。如果您有任何疑问,请在下面留言,我们将在 24 小时内回复。
如果你没有任何疑问,并且喜欢阅读它,或者它以任何方式帮助了你,那么请为它鼓掌。它在这个令人沮丧的新冠肺炎时代激励着我。
平安无事:)
理解 Google 的 Big bird——它是 NLP 的另一个重要里程碑吗?
谷歌研究人员最近在 arXiv 上发表了一篇题为《大鸟:更长序列的变形金刚》的论文。
图片由皮克斯拜的 Gerd Altmann 提供
去年,谷歌的研究人员发布了 BERT,这被证明是自 RankBrain 以来最高效和最有效的算法变革之一。看看最初的结果,BigBird 也有类似的迹象!
在本文中,我介绍了:
- 基于变压器的模型的简要概述,
- 基于变压器的模型的局限性,
- 什么是大鸟,还有
- 大鸟的潜在应用。
我们开始吧!
基于变压器的模型概述
自然语言处理(NLP)在过去的几年中有了很大的改进,基于变形金刚的模型在其中扮演了重要的角色。尽管如此,仍有许多事情有待发现。
Transformers 是 2017 年推出的自然语言处理模型,主要用于提高处理和理解文本翻译和摘要等任务的顺序数据的效率。
与在输入结束之前处理输入开始的递归神经网络(RNNs)不同,变压器可以并行处理输入,从而显著降低计算的复杂性。
BERT 是 NLP 中最大的里程碑式的成就之一,它是一个开源的基于 Transformers 的模型。一篇介绍 BERT 的论文,与大鸟一样,由谷歌研究人员于 2018 年 10 月 11 日发表。
来自变压器的双向编码器表示(BERT)是基于变压器的高级模型之一。它在大量数据(预训练数据集)上进行预训练,BERT-Large 在超过 25 亿个单词上进行训练。
话虽如此,BERT 是开源的,允许任何人创建自己的问题回答系统。这也有助于它的广泛流行。
但是 BERT 不是唯一的上下文预训练模型。然而,与其他模型不同,它是双向的。这也是其成功和应用多样的原因之一。
这个预训练模型的结果绝对令人印象深刻。它被成功地用于许多基于序列的任务,如摘要、翻译等。甚至谷歌也采用 BERT 来理解其用户的搜索查询。
但是,像其他基于变形金刚的模型一样,BERT 也有其自身的局限性。
以前基于变压器的模型的局限性
虽然基于变压器的模型,尤其是 BERT,比 rnn 有很大的改进和效率,但它们也有一些限制。
伯特工作在一个完全自我关注的机制上。这导致每个新输入令牌的计算和存储需求呈二次增长。最大输入大小约为 512 个令牌,这意味着该模型不能用于大型文档摘要等任务的较大输入&。
这基本上意味着在将一个大字符串作为输入应用之前,必须将其分解成更小的片段。这种内容碎片还会导致大量的上下文丢失,从而限制了它的应用。
那么,什么是大鸟,它与 BERT 或者其他基于变形金刚的 NLP 模型有什么不同?
介绍大鸟—适用于更长序列的变压器
如前所述,BERT 和其他基于变形金刚的 NLP 模型的一个主要限制是,它们运行在一个完全的自我关注机制上。
当谷歌的研究人员在 arXiv 上发表了一篇题为 “大鸟:更长序列的变形金刚” 的论文后,这种情况发生了变化。
BigBird 运行在一种稀疏注意力机制上,这种机制允许它克服 BERT 的二次依赖性,同时保留完全注意力模型的属性。研究人员还提供了大鸟支持的网络模型如何超越以前的 NLP 模型以及基因组学任务的性能水平的实例。
在我们进入 BigBird 的可能应用之前,让我们来看看 BigBird 的主要亮点。
大鸟的主要亮点
以下是 BigBird 的一些特性,这些特性使它优于以前基于 transformer 的模型。
- 稀疏注意机制
假设您收到一张图片,并被要求为其创建一个相关的标题。你将从识别图片中的关键物体开始,比如说一个扔“球”的人。
作为人类,识别这个主要对象对我们来说很容易,但是为计算机系统简化这个过程在 NLP 中是一件大事。注意力机制被引入来降低整个过程的复杂性。
大鸟使用稀疏注意力机制,这使它能够处理
序列长度比使用 BERT 可能达到的长度多 8 倍。请记住,这个结果可以使用与 BERT 相同的硬件来实现。
在 BigBird 的上述论文中,研究人员展示了 BigBird 中使用的稀疏注意机制如何与完全自我注意机制(在 BERT 中使用)一样强大。除此之外,他们还展示了“图灵完全编码解码器有多稀疏”。
简而言之,BigBird 使用稀疏注意机制,这意味着注意机制是一个令牌一个令牌地应用的,不像 BERT 那样,注意机制只应用于整个输入一次!
- 可以处理长达 8 倍的输入序列
BigBird 的一个关键特性是它能够处理比以前长 8 倍的序列。
研究小组设计了 BigBird,以满足像 BERT 这样的完整变形金刚的所有要求。
使用大鸟和它的稀疏注意力机制,研究小组将 O(n)(伯特的)复杂性降低到仅为 O(n)。这意味着限制为 512 个记号的输入序列现在增加到 4096 个记号(8 * 512)。
创造大鸟的研究人员之一 Philip Pham 在一次 黑客新闻讨论 — 中说,“在我们的大部分论文中,我们使用 4096,但我们可以使用更大的 16k+”
- 根据大型数据集进行预训练
谷歌研究人员在大鸟的预训练中使用了 4 个不同的数据集——琐事-QAHotpotQA-分心物&WikiHop。
虽然 BigBird 的集体预训练数据集远不如 GPT-3 的大(在 1750 亿个参数上训练),但研究论文的表 3 显示,它的表现优于 RoBERTa(一种稳健优化的 BERT 预训练方法)和 Longformer(一种用于长文档的类似 BERT 的模型)。
当一位用户要求 Philip Pham 比较 GPT-3 和大鸟时,他说—“GPT-3 只使用 2048 的序列长度。“大鸟”只是一种注意力机制,实际上可能是 GPT-3 的补充。”******
这篇文章讲述了这个疫情如何给了人工智能-人工智能阿朵急需的推动,以及这个新发现的…
towardsdatascience.com](/the-story-of-newly-found-friendship-ai-ml-and-the-pandemic-6ceb02376ebd)
大鸟的[可能]应用
一篇介绍大鸟的论文是最近才推出的——2020 年 7 月 28 日。因此,大鸟的全部潜力还有待确定。
但是这里有几个可能的应用领域。其中几个应用也是 BigBird 的创作者在 原创研究论文 中提出的。
- 基因组处理
深度学习在基因组数据处理中的使用有所增加。编码器将 DNA 序列片段作为任务的输入,如甲基化分析、预测非编码变体的功能效应等。
BigBird 的创建者说: “我们引入了一种基于注意力的模型的新应用,其中长上下文是有益的:提取基因组序列(如 DNA)的上下文表示”。
在使用 BigBird 进行启动子区域预测时,该论文声称已经将最终结果的准确性提高了 5%!
- 长文档摘要&问答
由于 BigBird 现在可以处理 8 倍长的序列长度,它可以用于 NLP 任务,如较长文档形式的摘要和问题回答。在大鸟的创建过程中,研究人员还测试了它在这些任务中的表现,并见证了“最先进的结果”。
- 谷歌搜索大鸟
谷歌于 2019 年 10 月开始使用 BERT 来理解搜索查询,并为其用户显示更多相关结果。谷歌更新搜索算法的最终目的是比平时更好地理解搜索查询。
随着 BigBird 在自然语言处理(NLP)方面优于 BERT,开始使用这一新发现的更有效的模型来优化 Google 的搜索结果查询是有意义的。
- Web &手机 App 开发
十年来,自然语言处理取得了显著的进步。有了一个 GPT-3 驱动的平台,它可以将你的简单语句转化为已经到位的功能性 web 应用程序(以及代码), 人工智能开发者 可以真正改变你开发 web & web 应用程序的方式。
由于大鸟可以处理比 GPT-3 更长的输入序列,它可以与 GPT-3 一起使用,为您的业务高效快速地创建 web &移动应用 。
结论
虽然关于 BigBird 还有很多有待探索,但它绝对有能力彻底改变自然语言处理(NLP)。你对大鸟及其对 NLP 未来的贡献有什么看法?
参考资料:
【1】曼齐尔·扎希尔和他的团队,《大鸟:更长序列的变形金刚》(2020),《T9》arXiv.org
2Jacob Devlin,张明蔚,Kenton Lee,Kristina Toutanova,BERT:用于语言理解的深度双向转换器的预训练,arXiv.org****
了解乳腺癌筛查数据
如何利用数据的流行病学基础来更好地训练和理解人工智能模型
2020 年的头几个月可能会成为现代史上最伟大的疫情的开端。来自世界各地的流行病学家争先恐后地想出紧急计划来控制这种疾病。巨大的生命损失掩盖了更好的消息:人工智能令人印象深刻的进展,特别是筛查性乳房 x 线照相术[1,2]。
人工智能在医学领域的成功应用通常至少与调整数据和调整模型一样重要。不管你给 VGG 或雷斯内特喂狗和猫的照片,他们都学不会在乳房 x 光检查中发现癌症。了解您所处理的数据的流行病学基础真的会有所不同。
然而,进入这个领域可能会令人生畏,因为它需要从过去 60 年的医学文献中提取精华。此外,该领域往往不像大多数人工智能从业者起源的工程或数学等硬科学那样结构化。
这篇文章旨在简要介绍(基于乳房 x 线摄影的)乳腺癌筛查数据及其主要方面,这些方面对于开发人工智能应用程序和理解各种最近关于乳房 x 线摄影的人工智能论文的结果非常重要。
乳腺癌筛查流程
乳腺癌是一种常见疾病:八分之一的女性会在一生中的某个时候患上乳腺癌。大多数欧洲国家、美国和一些亚洲国家通过定期乳房 x 光检查(x 光图像)对超过一定年龄的无症状妇女进行乳腺癌筛查。几项随机对照试验显示死亡率显著降低,因此筛查可以挽救生命[3]。这个筛选过程的执行方式因国家而异。大多数欧洲国家都有国有化的项目,由政府机构用一封信邀请妇女,美国没有。
如果一名妇女参加筛查,乳房 x 线照片会被记录下来。如果这项检查发现可疑,她被召回进行诊断检查,其中可能包括进一步的成像。如果这仍然是不确定的,进行活检。在一些国家,女性会跳过官方邀请,直接进行诊断检查,这被称为灰色筛查。图 1 提供了一个示例。
图一。乳腺癌筛查流程概述,即在欧洲普遍执行的方式。在一些国家(如德国),并非所有被邀请的妇女都参加常规筛查,但有时会投机性地进行诊断性乳房 x 光检查。这被称为“灰色筛选”。(图片作者提供)
除了确切的过程,筛选政策的参数也因国家而异。下面是几个重要的变量。
首次筛查年龄
患癌症的风险随着年龄的增长而增加。在年轻时进行筛查通常是不必要的。例外情况是高危人群中的妇女(例如,因为基因突变使其更有可能患乳腺癌),她们通常从比普通人群更年轻的年龄开始筛查,并进行补充成像,如 MRI。除了风险之外,组织成分会随着年龄的增长而变化,这使得年轻女性很难发现癌症。在大多数欧洲国家,筛查从 50 岁开始(尽管有些例外是瑞典和英国)。在美国和东亚,这一比例通常为 40。
当一名妇女第一次接受筛查时,发现癌症的几率更高,因为你会发现癌症已经发展了几年。第一轮和随后的筛查通常分别称为患病率和发病率筛查。
筛选轮次之间的时间
筛查之间的时间,或筛查间隔从美国的一年到英国的三年不等,在英国大多数欧洲国家使用两年的间隔。与筛查开始和筛查周期的年龄类似,间隔时间在一定程度上决定了每次筛查中检测到(或可能检测到)的癌症数量。图 2 对此进行了说明。
y 轴代表人群中可检测到的癌症数量,x 轴代表时间。一个人在筛查之间等待的时间越长,被检测出的癌症就越多。请注意,这是一种简化,在实践中,并不是群体中的所有女性都被同时筛查。还要注意的是,可检测到的癌症数量不会下降到零:有些癌症被遗漏了,我们将在这篇文章的后面谈到这一点。
图二。说明两种不同的筛查间隔及其对可检测癌症数量的影响。红线表示可检测到的癌症数量,黄线表示一轮筛查。你在两次检查之间等待的时间越长,你发现的癌症就越多。请注意,“一轮筛查”是指所有患者同时接受筛查的单一事件。实际上当然不是这样(图片由作者提供)。
除了上面提到的变量之外,还有一些参数可以通过“比率”来获取,这些参数主要是(除了参与率之外)在计划的敏感性和特异性之间进行权衡的结果。以下是一些常用的费率。
- 参与率在全国性的筛查项目中,政府通常会发出邀请函。参与率描述了实际参加筛查的女性比例,通过所有收到邀请的人进行标准化。请看图 3,我们可以用橙色圆圈除以灰色圆圈来得到参与率。
- 召回率在所有参与筛查的女性中,只有一小部分被召回(被邀请进行后续检查)。这个比例很大程度上取决于筛查项目和/或中心。在美国,召回率通常很高:约为 10%,而在欧洲,这一数字接近 3%。患病率筛查的召回率通常也更高。在图 3 中,用暗红色的圆圈除以橙色的圆圈可以得到召回率。
- 活检率当女性被召回进行后续检查时,他们有时会进行第二次乳房 x 光检查(诊断性乳房 x 光检查),进行超声波检查或活检,如果他们仍然不确定的话。大约 50%的诊断检查会导致活检。活检率是通过将图 3 中的蓝色圆圈除以深红色圆圈获得的。
- 癌症检出率癌症检出率(CDR)就是每 1000 名患者中检出的癌症数量。再次使用图 3 中的斑点,这是通过取亮红色圆、亮蓝色圆的交集并除以橙色圆获得的。就像召回率一样,CDR 通常在患病率筛查上更高。
图三。筛选过程中不同“比率”的说明。大的灰色斑点代表该年龄组中所有接受筛查的女性,橙色圆圈代表所有实际参加筛查的女性,深红色圆圈代表所有被召回的女性,亮蓝色圆圈代表所有接受活检的病例,亮红色圆圈代表所有癌症(图片由作者提供)。
错过的癌症
注意,在图 2 中,可检测的癌症的数量在一轮筛选后没有下降到零,并且在图 3 中,亮红色斑点不是暗红色斑点的子集。这是为了说明癌症被遗漏了。其中一些是在两次筛查之间检测到的,这些被称为间期癌。有些在下一次筛查时被查出。
间断性癌症通常用于评估筛查项目的质量。如果有很多,这意味着许多癌症可能在筛查过程中被遗漏,灵敏度太低。然而,间隔癌症并不直接意味着在筛查过程中遗漏了癌症。研究表明,约 20-30%的间隔期癌症和下一轮筛查中检测到的大致相同数量的癌症被遗漏[4,5,6](即,在先前的乳房 x 光片中可检测到)。这是因为癌症可以在最后一轮筛查后开始生长,或者在乳房 x 光片上根本看不到。图 4 提供了这一过程的图示。
图 4。乳腺癌筛查中癌症检测过程的图示。左侧的亮红色圆圈表示在筛查时间 t 内检测到的所有癌症(筛查检测到的癌症)。中间的斑点是一组间隔期癌症,右边的斑点是在下一轮筛查中检测到的癌症(时间 t +间隔期 I)。大约 20 到 30 个间歇期癌症和在下一轮筛查中检测到的癌症在先前的筛查中已经是可见的。所有亮红色斑点代表时间 t 时所有可检测的癌症(图片由作者提供)。
定义标签
定义标签很重要,没有它,人工智能模型就不能很好地学习。对于医学数据来说,这通常没有自然图像简单,因为并不总是清楚“真相”是什么。有时会区分参照标准和黄金标准。在第一种情况下,这是一组读者的最佳猜测,在第二种情况下,它具有一些更多的信息,例如来自组织病理学的信息,读者不能仅通过查看图像来知道。
不幸的是,选择哪一个也不是那么明显。例如,当构建用于筛查的人工智能工具时,你应该只检测癌症还是检测放射科医生认为可疑的一切?如果您选择前者,并使用该工具来协助放射科医生,他们可能会认为奇怪的是,该系统没有标记他们认为非常可疑的东西。但是,如果系统只能访问相同的信息,要比放射科医生好得多就很难了。下面将进一步解释这两种选择。
比拉德分数—参考标准
在大多数欧洲国家,考试是由两个认证的放射科医生阅读。在美国,这通常只是一个人。看完试卷后,她给出一个分数:比拉德分数[7]。这是在乳房 x 线照相术的国际标准化报告系统中定义的等级。这些分数可以解释为[8]:
- 这是个好消息,考试是空的,没有发现。
- 这也是好消息,但是发现了一些良性异常。
- BIRADS 3 在这种情况下,放射科医师不确定,需要半年左右的随访。这个分数在一些国家是不允许的,因为读者会倾向于过多地使用它。
- BIRADS 4a 这意味着在乳房 x 光片上发现了可疑之处,需要进行诊断检查。然而,这实际上是癌症的可能性仍然很小。
- 这意味着患癌症的几率比 4A 略高。这个分数也不是在所有国家都使用,因为 4A 有足够的理由召回这个女人。
- BIRADS 4C 发现了一个非常可疑的病变,读者非常确定这个检查描述的是癌症。
- 这意味着发现了一种或多种教科书上的癌症,并且读者非常确定检查描述的是恶性肿瘤。
从技术上来说,BIRADS 分数不是一个序数量表,因为 BIRADS 1 和 2 具有相似的可疑程度。因此,不应如此解释。
组织病理学结果—金标准
如果诊断检查不能排除癌症,临床医生进行乳腺活检。这通常通过(真空辅助)针芯活检来完成,由此从可疑部位提取一小块组织。放在显微镜下的组织以确定它到底是什么。病理学家通常同意它是否是癌症,但对确切的亚分类有不同意见。
常见的(乳腺癌)分类系统是根据它们的起源组织。乳房中的大多数癌症是癌,这意味着它们起源于上皮组织。乳房由小叶和导管组成,负责乳汁的产生和运输。癌症可以是原位的,这意味着它们没有增殖到其来源组织之外,或者是侵入性的/浸润性的,这意味着癌细胞开始迁移。
取其笛卡儿积,我们得到四种主要类型:浸润性导管癌(最常见)、浸润性小叶癌、导管原位癌和小叶原位癌。在最近的乳腺病理学指南中,最后一种不再被认为是真正的癌症[9]。侵袭性癌症是最危险的,绝对不应该被人工智能系统遗漏。这只是冰山一角,还有其他几十种病理和分类系统[9]。
定义积极和消极
阳性的最常见定义是简单地观察组织病理学结果,即是否为癌症。然而,通过这样做,我们还没有到达那里。因为筛选过程具有时间成分,我们还需要定义何时获得特定结果。(至少)有三种选择:
检查后立即通过活组织检查确诊癌症这是阳性的最常见定义。然而,使用这个定义,很难表明一个算法比人类具有更高的灵敏度,除非你通过阅读来区分它们,或者让读者再次阅读相同的案例。
这个定义相当于只取筛查出的癌症,图 4 中左边的大红色斑点。相应的人的操作点在图 5 的 ROC 图中用红叉表示。
癌症通过活组织检查确认,直到下一次筛查检查这通常用于确定筛查程序的灵敏度,因为它很容易从癌症注册表中计算出来。你可以简单地将筛查检测到的癌症数量除以筛查检测到的+间隔期癌症。这通常会导致 70–80%的灵敏度。尽管它在评估研究中并不常用。
在图 4 中,这意味着将左边的大红色斑点和中间的两个斑点中较大的一个作为阳性。相应的人工操作点由图 5 中的黄色十字表示。
在下一次筛查检查后三个月,通过活检确诊癌症deep mind 在最近的《自然》杂志论文中使用了这个定义【1】。使用这种方法的原因是他们称之为“看门人效应”的东西:通过只将筛查出的癌症视为阳性,你会使结果有偏差,并使人类看起来更好。然而,另一方面,许多将在未来几年发展的癌症在当前的检查中还不可见,这意味着敏感性显著下降。
我们通过将图 4 中的所有大斑点视为阳性来得到这个定义。相应的人工操作点是图 5 中的蓝色十字。
最后一个也可能是最纯粹的定义阳性的方法是简单地观察所有“可检测的癌症”(图 4 中所有的亮红色斑点)。然而,这可能是乏味的,因为它需要确定错过了什么,这只能通过再次阅读案例来评估。
图五。ROC 图中不同阳性定义的不同人为操作点的图示。红色:只有筛查出的癌症被认为是阳性。黄色:筛查出的和间隔期的癌症为阳性。蓝色:检查后两年内发现的癌症被视为阳性(图片由作者提供)。
对人工智能研究的启示
那么这一切和人工智能有什么关系呢?首先,如何定义数据以及如何将数据呈现给模型非常重要。这是人工智能产品的秘密成分的一部分,所以我不会在这里详述。在评估模型并将结果与文献进行比较时,考虑上述变量也是很有趣的。以下是最近文献中的几个例子:
- 如上所述,DeepMind 最近在《自然》杂志上发表的论文[1]使用了一个有点不寻常的积极的定义。这意味着这篇论文的结果看起来比其他论文差很多(包括读者和算法),这让许多人感到困惑。
- 乳房 x 线摄影梦想挑战[10]的最新评估论文展示了两个数据集的模型结果。第一个是由挑战组织者提供的美国数据集,第二个是瑞典数据集。该模型是在第一个数据集上开发的,但当应用于第二个数据集时,表现出更好的性能。作者假设(除其他因素外),这是因为第二组筛查的筛查间隔和癌症构成更长:筛查间隔时间越长,发现的浸润性癌症越多。这些可能更容易被发现。
- 一些人工智能论文结合了筛选和诊断数据[2,11]。虽然癌症在诊断和筛查检查中可能看起来几乎一样,但从诊断数据而不是筛查数据中获得阴性结果可能意味着“更难”(当然也取决于你如何训练你的模型)获得阴性结果,因为这些结果会在常规筛查中被召回,这意味着其中可能有可疑之处。这可能低估了模型在纯筛选数据上的表现。
- 美国和欧洲的筛查存在相当大的差异,这使得方法的比较变得困难。美国通常每年筛查一次,召回率更高。美国的癌症检出率更高,这意味着更多微妙的癌症可能在阳性结果中,可能更难检测到。Yala 等人[12]报告的 AUC 值约为 0.8,与欧洲的研究相比可能显得较低,但可能只是更硬的数据的结果。
总之,乳房 x 线摄影人工智能的最新进展令人兴奋,可能意味着朝着更好的医疗保健迈出了一大步。将数据中的临床和流行病学因素考虑在内有助于理解结果并提高性能。然而,比较研究仍然很复杂。目前最好的比较可能是简单地将每种方法与放射科医生进行对比[13]。
参考文献
[1]麦金尼、西耶克、戈德博勒、戈德温、安特罗波娃、阿什拉菲安、巴克、切苏斯、克拉多、乔治·c、达尔齐和埃特马迪,2020 年。乳腺癌筛查人工智能系统的国际评价。自然, 577 (7788),第 89–94 页。
2 Kim,H.E .,Kim,H.H .,Han,B.K .,Kim,K.H .,Han,k .,Nam,h .,Lee,E.H .,Kim,E.K .,2020 年。使用人工智能的乳腺 x 线摄影中癌症检测和假阳性回忆的变化:一项回顾性、多读者研究。《柳叶刀》数字健康, 2 (3),第 e138-e148 页。
[3] Marmot,M.G .,Altman,D.G .,Cameron,D.A .,Dewar,J.A .,Thompson,S.G .,Wilcox,m .,2013。乳腺癌筛查的益处和危害:一项独立综述。《英国癌症杂志, 108 (11),第 2205–2240 页。
[4]hov vind,s .,Geller,b .,Vacek,P.M .,Thoresen,s .和 Skaane,p .,2007 年。使用欧洲指南评估挪威乳腺癌筛查项目。《欧洲流行病学杂志》, 22 (7),第 447 页
[5]霍夫、亚伯拉罕森、萨姆塞特、杰赫、维格兰、克莱普和霍夫温德,2012 年。乳腺癌:错过的间隔和筛查-全数字化乳腺摄影和屏-片乳腺摄影检测到的癌症-来自一项回顾性综述的结果。放射学, 264 (2),第 378–386 页。
[6]n . Perry,m . Broeders,c . de Wolf,s . t rnberg,r . Holland 和 von Karsa,l .,2008 年。欧洲乳腺癌筛查和诊断质量保证指南。—总结文件。肿瘤学年鉴, 19 (4),第 614–622 页。
[7] Sickles,EA,D'Orsi CJ,Bassett LW,等. ACR BI-RADS 乳房 x 线照相术.载于:ACR BI-RADS 图谱,乳腺成像报告和数据系统。弗吉尼亚州雷斯顿,美国放射学院;2013.
[8]https://www . Hopkins medicine . org/breast _ center/
[9]朱利亚诺、埃奇、S.B .和奥尔托巴依、G.N .,2018 年。AJCC 癌症分期手册:乳腺癌。外科肿瘤学年鉴, 25 (7),第 1783–1785 页。
[10] Schaffter,t .,Buist,D.S .,Lee,C.I .,Nikulin,y .,Ribli,d .,Guan,y .,Lotter,w .,Jie,z .,Du,h .,Wang,s .和 Feng,j .,2020 年。结合人工智能和放射科医师评估来解释筛查乳房 x 光片的评价。 JAMA 网开, 3 (3),pp.e200265-e200265。
[11] Rodriguez-Ruiz,a .,Lå ng,k .,Gubern-Merida,a .,Teuwen,j .,Broeders,m .,Gennaro,g .,Clauser,p .,Helbich,T.H .,Chevalier,m .,Mertelmeier,t .和 Wallis,M.G .,2019 年。能否通过人工智能自动识别正常检查来减少乳腺 x 线筛查的工作量?可行性研究。欧洲放射学, 29 (9),第 4825–4832 页。
[12]亚拉,a .,舒斯特,t .,迈尔斯,r .,巴兹莱和雷曼,c .,2019 年。一个深度学习模型来分类筛查乳房 x 光片:一项模拟研究。放射学, 293 (1),第 38–46 页。
[13] Lotter,w .,Diab,A.R .,b .,Kim,J.G .,Grisot,g .,Wu,e .,Wu,k .,Onieva,J.O .,Boxerman,J.L .,Wang,m .和 Bandler,m .,2019 年。使用注释有效的深度学习方法在乳房 x 线照相术和数字乳房断层合成中进行鲁棒的乳腺癌检测。 arXiv 预印本 arXiv:1912.11027 。
理解中心性度量
中心性度量是让您更好地了解数据集的工具。它们指向你的分布中心。
中心性度量在数据分析的探索阶段非常有用。在对数据管道的输出进行健全检查时,或者在开始回答一个新产品问题时,您可能会用到它们。
当你读完这篇文章的时候,你会意识到你已经使用中心性度量很长时间了,甚至没有注意到。
为什么您应该关注中心性指标
我们每天都会在某个时候查看数据,无论是在工作中还是在查看我们最喜欢的运动队的数据。
如果您是一名数据科学家,并且正在用数据回答一个产品问题,中心性指标可以帮助您:
- 显示您正在处理的数据的分布中心。
- 定义更简单、更容易解释的指标。大多数时候,您只需要将正确的中心性指标应用到您的问题的上下文中。
- 传达您正在处理的数据的一些重要特征。
- 解读仪表板并发现是否有问题。
这些度量将你指向分布的中心。它们告诉你大多数数据点聚集在哪里。
对于这些图中的每一个,您认为大多数数据点接近什么值?
我们将学习如何回答这个问题。
我们可以用中心性度量来回答这个问题,例如,通过计算
- 平均
- 中位数
- 方式
如果这些名字听起来很陌生,不要担心!我相信你从小就使用过这些指标。你甚至可以根据它们做决定。
让我们来看看这些中心性指标中的每一个。
平均
我们都知道这是平均值
你可能不会称之为卑鄙,但你可能熟悉平均的概念。
例如,如果我告诉你一个 MLB 球员在的平均击球率是 0.3,你就会知道我是通过以下方式得出这个数字的:
这就是我们所说的平均,花哨的数学名称是算术平均。简而言之,大家都称之为卑鄙。
要计算平均值,您需要:
- 将你感兴趣的所有观察值相加,比如一个 MLB 玩家的总点击次数,
- 用它除以你观察到的总数,就像,蝙蝠的总数。
如果我们要使这个公式更通用,我们应该:
其中 obs_1 到 obs_n 为你关心的观察值,n 为你看到的观察值总数。
这就是每个人提到手段时都在谈论的,但是,如果你好奇的话,还有几种其他类型的手段。
诠释意义
当我告诉你一个 MLB 球员的平均击球率是 0.3 时,我的意思是每 10 次击球中,那个球员会击中其中的 3 次。
这是一个令人敬畏的见解,因为现在你有一种方法来比较不同球员的击球率。
这种方法还有一个有趣的特性,它的工作原理就像一种秤或者一场拔河比赛。添加过高或过低的数据点,平均值将向那个方向移动。
如果我给你看一个例子,就很容易发现这一点。
你想学习新的聚会技巧,所以你决定练习倒立。为了看看你是否有进步,你也要计时你能倒立多长时间。
在练习的第一天,你尝试做 5 次倒立:
- 有一次你倒立了一秒钟,
- 下一次你拿着它两秒钟的时候,
- 然后持续 3 秒钟,
- 随着你的进步,接下来的几次你保持 4 秒,然后 5 秒。
第一天倒立练习后的数据集。
第一天练习后,如果你要计算倒立的平均时间,你会做:
太好了,你倒立平均保持了 3 秒钟!
在接下来的练习中,你感觉不舒服,所以你只做了几次倒立。第一次尝试时,你保持 1 秒钟,第二次尝试时,你保持倒立 3 秒钟。
如果我们查看到目前为止您所有练习数据的数据集,我们有:
- 上次练习的数据点,
- 两个新的数据点,聚集在 1 和 3 周围。
第二天倒立练习后的数据集。
你认为这些新数据点将如何影响平均值?让我们计算一下最新的保持时间。
您已经向数据集添加了两次以上的尝试,但是这些保持时间更短。因此,您的平均等待时间现在更短了。
但是你对目前的成绩不满意,想改进你的倒立游戏。所以你决定休息一会儿,改天再练习。
在下一次练习中,你首先做一个倒立 1 秒钟,然后再做一个 3 秒钟,然后打破你的个人记录:做一个倒立 10 秒钟!
第三天倒立练习后的数据集。
请记住,平均值就像一场拔河比赛,所以如果您添加的数据点与您之前看到的数据点大相径庭,平均值就会被推向这些值。
在第三个练习中,您添加了两个数据点,即 1 秒和 3 秒的观察值,这两个数据点将平均值拉向一个较低的值。如果您停在那里,您的平均等待时间确实会更短:
但是你用 10 秒倒立打破了你的个人记录。这个保持时间比你之前的最好时间要长,所以你的平均保持时间会增加。
我们可以说平均值对极端观测值很敏感,因为它向数据集中更极端的值移动。
中位数
中间的数据点
为了计算中位数,你要寻找正好在分布中间的点。所以你要:
- 对数据集进行排序。
- 取中间的值。
- 如果数据集中间有两个数字,求它们的平均值。
从第一天的实践中获取数据集,我们想把它分成大小相等的两部分。我们可以通过分离数字 3 来实现。
第一次练习后倒立保持时间的中位数。
这是我们的中位数,3 秒,这意味着:
- 在这个练习的一半尝试中,你做了长达 3 秒钟的倒立,
- 另一半时间,你手倒立 3 秒以上。
现在,如果我们看看练习的第三天,中间值就不那么容易发现了。我们实际上有两个值,在分布的中间。但是没关系,我们可以将它们平均来计算中位数。
第三次练习后倒立保持时间的中位数。
中位数的力量在于你现在用百分比来思考。您不需要知道数据集的大小来完全解释您的结果,因为您知道 50%的分布将低于中位数,而另外 50%将高于中位数。
如果你仔细观察,当 10 秒倒立将平均值从 3 秒提高到 3.3 秒时,中位数保持不变。这就是为什么我们可以说,中位数对极端的观察更稳健。
方式
最受欢迎的数据点
该模式捕获数据集中最频繁出现的值。
让我们再回顾一遍倒立练习的第一天,画出保持时间的直方图。我们可以看到,在我们的例子中,有两个值是最常见的 1 秒和 3 秒保持时间。
第一次练习后倒立保持时间直方图。
你可能认为,像均值和中值一样,众数必须是一个单一的值。但事实并非如此。该模式将显示更频繁出现的值,它将告诉您分布向哪个值倾斜。
例如,第一次练习的保持时间分布有两个峰值,因为有两个值是最常见的。
在 1 秒内有一个峰值,在 3 秒保持时间内有另一个峰值。因为我们的分布有两个模式,我们说它是双峰的。但是,如果我们的分布只有一个峰值,如一个模式,它将是一个单峰分布。
因为重点是频率而不是值的顺序,所以不需要对数据集进行排序来计算模式。
而且,像中位数一样,该模型对异常值也是稳健的。
如果我们看一下第三次练习,即使记录保持时间为 10 秒,模式也保持不变。我们仍然可以看到 1 秒和 3 秒处的两个峰值,因为这是我们数据集中最常见的值。
第三次练习后倒立保持时间直方图。
该模式的另一个特点是它不局限于数值数据集。您也可以计算分类数据的模式。
如果您的数据集是关于在美国不同州度过的假期,您可以计算数据集的模式。过程是一样的,你绘制直方图或者你去过每个州的次数,模式是你去过最多的州。
既然您对中心性度量有了更多的了解,回答这个问题就很容易了。
对于这些图中的每一个,您认为大多数数据点接近什么值?
现在你知道我们是如何得到这些平均值、中间值和众数的了😀
感谢阅读!
理解链式法则——一种直观的方法
图片来自https://unsplash.com/@kaleyloved,来源:https://unsplash.com/photos/gtVrejEGdmM
这篇文章是针对那些在理解链式法则上有困难的高中生或大学生的。我试图用一种直观而又不同的方式来解释一个概念,同时为将来会对你有所帮助的‘全微分’概念打下基础。尽管如此,如果你只是对数学感兴趣,那么这篇文章也适合你!
这是一个微积分系列的第一部分,在这里我将解释一元问题和多元问题的链规则的概念(在引入偏导数之后)。我打算通过提供“材料导数”概念的直观感受来结束这个系列。
在数据科学中,链规则是一个非常重要的概念,尤其是如果你想了解像神经网络这样的概念是如何工作的。
我会假设你从头到尾都知道什么是“导数”。
什么是链式法则?
链式法则是微积分中的一个概念,其中一个复合函数的导数不仅仅是母函数的导数,它还受到其子函数导数的影响。
那么这看起来像什么?
这个问题有点抽象不是吗?我们先简化一下,再赋予它意义。
假设你正试图衡量你的幸福😊变化,碰巧食物🍞让你开心,听起来很合理,对吗?
我理解你可能会觉得用表情符号来代表变量令人困惑,比你想象的要多(这发生在我们大多数人身上!).我将在开始写数学形式,但是我鼓励你‘打破符号’,试着理解概念是什么,而不是‘记忆’数学!
自然,真实的模型比上面描述的要复杂得多,但是我们都可以与之相关。当没有食物时,我们不快乐。当我们有足够的食物时,我们非常满足。当食物太多的时候,我们就不开心。
现在假设,食物🍞是时间的函数,所以它看起来像这样(当你有非常饥饿的家庭成员时尤其如此…):
现在,我对测量我的快乐随着时间的变化非常感兴趣。本质上:
我敢肯定,你的老师/讲师已经对你进行了训练,“你必须使用链式法则,因为🕒不是的直接变量😊。所以:
你有没有想过为什么这行得通?或者,如果我们的幸福方程式中有另一个变量,会发生什么?如果我们的幸福也依赖于其他东西呢?
另一种观点…全微分
考虑全微分的概念。对于一个简单的单变量问题(在我们的例子中,幸福😊只有 T1 是食物的功能吗🍞),意思是:
的总变化😊由的导数给出😊相对于🍞、乘以乘以中的变化🍞。
所以:
但是为什么我们首先要用全导数的定义呢?的导数有意义吗😊关于🍞,乘以中的变化🍞给了我们变化在😊?
让我们进一步研究这个问题:
既然我们现在理解了为什么全微分有意义,我们可以开始应用它了。
我们知道快乐😊是食物的一个功能🍞,所以我们可以用幸福的全微分来写:
我们也知道食物🍞是时间的函数🕒,所以我们可以用食物的总导数来写:
替代食物中的变化🍞在第一个表达式中,通过第二个表达式,我们得到最后一个表达式:
现在,我们再次调用时间变化接近 0 的假设。这允许我们将弯曲的 d 转换成直的 d,所以:
如果您对曲线 d 如何变成直线 d 感到困惑,请查看此动画:
我们现在已经到了链式法则的表达式了!
现在来应用它…
好了,我希望你现在已经更好地理解了链式法则的概念,它是如何产生的,以及它为什么有意义。
我留给你一个问题去解决…
我用来代表快乐和食物的曲线如下:
利用链式法则,你能找到快乐随时间的变化吗?
读者注意:
我希望你喜欢这篇文章。我希望能启发你,不要把数学看作是学校里的一门无聊的课程,或者一堆丑陋的符号……而是一种美丽的语言,你可以用它从不同的角度看世界。
我希望我介绍这个概念的相当“非正统”的方法有所帮助。
如果你喜欢我的内容,请关注我,因为这将激励我创造更多…不要羞于通过评论提供反馈!我总是希望学习新的东西;)
了解聚类
让数据科学家理解机器学习的简单笔记
从复杂数据集中自动提取自然群体
来源( Unsplash )
什么是集群
聚类是一种无监督的学习技术,用于从预定义的类和先验信息中提取自然分组或标签。这是一项重要的技术,用于探索性数据分析(EDA)以从数据中发现隐藏的分组。通常,我会使用聚类来发现关于数据分布和特征工程的见解,从而为其他算法生成一个新类
聚类在数据科学中的应用
电子商务中的卖家细分
当我在 Lazada(电子商务)实习时,我处理 3D 聚类来找到卖家的自然分组。Lazada 销售团队要求进行分析,以通过多种促销和徽章奖励表现出色的销售人员。然而,要做到这一点,我们需要了解卖家是谁,他们的表现如何。为了做到这一点,我们设计并选择了三个主要维度:客户评论、交付和退款质量,以及产品供应(专业卖家或普通卖家)。
基于这些主要标准,我们创建了 3D 聚类可视化来识别我们的卖家。根据结果,我们对这些卖家进行了分类,并为每个卖家创建了奖励和徽章跟踪。最终,通过 AB 测试,我们提高了 23%的关键销售业绩,并改善了一般用户体验指标。
在 Lazada 使用 Scikit Learn 和一般关键销售业绩进行三维聚类。这是一个例子,而不是避免 NDA 冲突的真实表示(保密协议)。
其他应用
如果我的这个亲身经历还不够的话,在我们的生活中你还可以找到更多的集群应用。
标签标注:假设你想对很多产品进行分类,比如相机、衣服和冰箱。你怎么知道给他们贴什么样的标签合适呢?相机、电视和台式机应该归入相同的标签吗?为了更好地区分这些产品,您应该创建多少个标签?
客户细分:假设你正在努力改善零售采购。你能根据买家的购买模式对他们进行分类吗?什么是适当的标签和促销,以增加每个客户群的支出?
推荐系统中的类型选择:假设网飞向我们推荐要观看的节目。每个推荐应该有哪些合适的标签或流派来分析用户的观看行为?在节目创作的时间段上对他们的观点进行分类是个好主意吗?
所有这些都需要聚类技术来创建自然分组,尤其是在多维数据复杂性中。
集群是如何工作的?
给定一组对象,我们希望对它们进行聚类;将他们分成一组。
我从 IMDB 电影数据中分离聚类
在这个图像中,你可以直接看到一些点的自然组合。这很容易想象,因为我们这里只有两个维度(y 轴和 x 轴)。因此,我们可以突出分组。但是假设我们有很多维度。我们如何使用这些特性将它们分组?
集群目标:
- 输入:一组物体:x .这是一个距离度量 D(。,.)其中它定义了物体间距离,使得 D(x,y) = D(y,x)其中 x,y E X。
- 输出:如果 x 和 y 属于同一个集群,对象的划分使得 Pd(x) = Pd(y)
这种由距离定义的相似性概念使人想起 k 近邻(KNN)。这意味着我们可以将对象填充到特定的簇中,而无需测量好簇或坏簇。重要的是我们用来创建分区和分隔组的距离函数。
这意味着解往往具有很高的方差,并且依赖于算法。每个聚类算法可能会得出不同的最佳聚类结果。在本文中,我们将讨论单链接集群,K 表示集群和软集群:
具有单个链接聚类(2)和 K 表示聚类(3)的未分组数据集
单连锁聚类
这是最简单的聚类算法。
基本 SLC 聚类步骤
给定 k 个聚类的输入:
- 我们将每个对象视为具有 n 个簇的簇
- 我们将类间距离函数定义为多个类中最近的可能距离
- 合并两个最近的集群
- 重复这个算法 n-k 次,生成 k 个聚类。
这种算法让我想起了“连点”游戏,这是一种将点一个接一个连接起来以构建特定形状和结构的游戏。
把这些点连接起来(来源)
复杂性和局限性
有趣的是,这种聚类算法拥有有趣的属性:确定性(它像 MST 最小生成树算法一样运行),因为它将距离视为边长,时间复杂度为 O(n)。我们需要在 n 次内评估 n 对(最坏的情况)。我们需要重复 k 次(n/2 ),但是我们还需要查看所有距离,以找到具有不同标签的最近的一对 O(n)。
单链接聚类还应用试探法将多个可能没有预期行为的接近点连接在一起。
k 均值聚类
k 表示聚类是一种简单直接的算法,因为每次迭代只需要少量的琐碎操作。它还确定收敛到局部/全局最小值,这意味着我们可以增加置信水平来信任结果。
基本 K 均值聚类步骤
- 选择 k 个随机质心
- 将每个点与最近的中心点相关联
- 通过平均聚类点来重新计算中心
- 重复这些步骤,直到它收敛。
k 均值优化函数
- 配置→中心,P
- 分数→P 中心和 x 的平方差总和。
- 邻域→离质心最近的点。
与 SLC 相比,范式发生了转变
与 SLC 相比,这需要一些范式转换,并且是现在世界上广泛使用的聚类算法。
- 取聚类中心(质心)的随机位置,而不是使用现有的观察值作为中心。
- 迭代每个质心的多个观测成员,直到看不到进一步的改进(收敛)
复杂性和局限性
就 O 符号而言,k 均值比 SLC 更有效——每次迭代的多项式时间为 O(kn)。
然而,也有我们需要考虑的缺点。第一个,k 中的 k 表示之前需要识别。这意味着,在运行该算法之前,我们需要首先通过使用肘方法等来分析最佳聚类数(k)。其次,我们放置的初始种子/质心也会极大地影响我们的聚类。第三个,离群点会极大地影响我们放置的质心,从而影响集群成员。最后,k 表示对假设的球形聚类进行聚类,这在一些非球形数据分布中可能是个问题。
以前我们只讨论了某个只有一个类作为其成员的观察。如果观察结果不明确怎么办?
软聚类
以前我们只讨论了某个只有一个类作为其成员的观察。如果观察结果不明确怎么办?然后我们将观察结果分成两组。
分配该观察的正确类别是什么?这仍不清楚。如果我们把这个观察分配给两个类呢?
这就是软集群的用武之地。它使用概率确定性来识别使用所选高斯分布的可能聚类。目标是最大化数据属于某一类的可能性。这被定义为期望最大化(EM 聚类)。
有趣的是,由于 EM 是基于概率的聚类,这意味着有无限多的配置。你永远不会做得更差,但你会不断接近,永远不会接近最终的最佳配置。
聚类算法的属性
那么我们如何选择使用哪个集群呢?对于我们的聚类算法,我们需要考虑以下属性。每个属性将根据我们使用的数据模式和距离矩阵而变化。
- 丰富度:对于将对象分配给聚类,存在某种距离矩阵 D,使得聚类收敛已经对给出最佳拟合的聚类数量设置了限制。如果我们在聚类未达到 k 时过早地停止 SLC 算法,我们将牺牲丰富性,因为我们限制了聚类的数量。
- 尺度不变性:假设我们用一个任意的常数(比如从摄氏温度到华氏温度)提升距离度量,聚类成员结果应该不会改变。如果聚类相距某个单位,缩放结果将会改变生成的聚类成员。如果我们在聚类相距预定值单位时过早地停止 SLC 算法,我们将牺牲尺度不变性。
- 一致性:通过压缩或扩展点,没有点会转移到另一个集群。如果聚类是具有最大识别距离的度数识别单元,那么我们可以转换这个距离公式并改变聚类成员。这意味着当我们使相似的点更相似或使不相似的点更不相似时,聚类不应该改变。如果当聚类是预定义的值单位除以最长的聚类内距离时,我们过早地停止 SLC 算法,我们将牺牲一致性。
理想情况下,您的集群应该具备所有这三个属性。但是,不幸的是,正如不可能定理中提到的,这是不可能的。请随意查看本文中的证明。
没有一种聚类方案可以同时满足这三个条件:丰富性、规模不变性和聚类性— 乔恩·克莱因伯格 15
最后…
我自己的聚类分析,使用各种聚类和特征变换技术
我真的希望这是一本很棒的读物,是你发展和创新的灵感来源。
请在下面的评论提出建议和反馈。就像你一样,我也在学习如何成为一名更好的数据科学家和工程师。请帮助我改进,以便我可以在后续的文章发布中更好地帮助您。
谢谢大家,编码快乐:)
免责声明:这是为我创建的复习笔记,用于更新我在佐治亚理工学院 OMSCS 课程中关于机器学习的知识。许多参考资料来自乔治亚理工学院的课堂和外部资源。
关于作者
Vincent Tatan 是一名数据和技术爱好者,拥有在 Google LLC、Visa Inc .和 Lazada 实施微服务架构、商业智能和分析管道项目的相关工作经验。
Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。
他一直积极咨询 SMU BI & Analytics Club,指导来自不同背景的有抱负的数据科学家和工程师,并为企业开发他们的产品开放他的专业知识。
最后,请通过LinkedInMedium或** Youtube 频道 联系文森特**
理解组合学:网格上的路径数
你能数出一个普通的 WxH 网格上有多少条路径吗?
B asic 组合学是编程人员不可或缺的工具,无论是开发人员、人工智能专家还是数据科学家。
在本文中,我们将解决一个简单的组合问题:计算从一个普通 WxH 网格的一个角到另一个角的路径数。
让我们从这个 3x3 的网格开始:
假设我们想从橙色方块到绿色方块。每一步我们都可能向右或向下移动。我们将用 R 表示向右移动的,用 D 表示向下移动的。我们感兴趣的是我们可以选择的不同路径的数量。让我们手动列举路径:
- RRDD
- DDRR
- RDRD
- DRDR
- RDDR
- DRRD
我们可以得出结论,在这个网格中有 6 条不同的路径。现在来看看这个 8×8 的网格:
如果你试图计算这个网格上的路径数,这将花费你相当长的时间。我们需要一些更聪明的数学方法。首先要注意的是,我们要走的步数并不取决于所走的路。
以步数计算的距离总是一样的。
这个距离也被称为 L1 距离、城市街区距离或曼哈顿距离,因为纽约市中心的街区是方形的。
另一方面,我们注意到在正方形网格上,由于对称性,R 移动的次数必须等于 D 移动的次数。此外,我们在每条路径上需要 7+7=14 步(你可以很容易地沿着网格的边界移动)。这两个要求使得以如下方式重新定义 8×8 栅格的问题成为可能:
找出字符串 RRRRRRRDDDDDDD 的不同排列的数目。
现在,排列的数量通常由 N 定义!或 N(N-1)(N-2)…2 ,也称阶乘。据此, N 为弦的长度。在我们的情况下,14!= 87178291200.然而,我们必须考虑这样一个事实,即 R 和 D 的顺序并不重要;他们是一样的。两者不同排列的数量是 7!= 5040.我们必须数两遍这个数字,因为 R 和 D 是无法区分的。换句话说,我们分 14!7 点前!*7!。这给了我们 3432,这是正确的答案。
为了将我们的公式推广到任何 NxN 网格,我们可以写成:
顺便说一下,还有一种更简单的写法:数字 D = N!/ (K!* (N-K)!)也叫二项式系数,我们可以写成(N 选 K) 。这个数字表示我们可以从一袋 N 个物体中挑选 K 个物体的方法的数量,不考虑挑选的顺序。因此,我们也可以把我们的公式写成:
更通用的 WxH 网格呢?在这种情况下,步数是(W-1) * (H-1),我们必须选择(W-1) R 步(你可以自己检查)。因此,公式变为:
很简单,对吧?注意,我们可以选择(H-1) D 步,而不是选择(W-1) R 步。直觉上,这应该会给我们相同的答案,而确实是这样。换句话说,这意味着二项式系数是对称的:
作为练习,尝试在 30x20 甚至 99x99 的网格上计算路径的数量。你也可以试着写一个递归计算所有路径的程序,但是这样的程序在 99x99 的网格上要花很长时间才能完成!
结论
我希望你在这篇文章中学到了一些实用的组合学,并且你将被激励去学习更多关于这个迷人的主题。
理解并发和多线程程序
迈克尔·泽兹奇在 Unsplash 上的照片
以及如何让你的程序变得高效
我最近不得不学习在 Go 中编程,因为我正在为浏览器开发一个多人游戏。
我在 reddit 的 r/gamedev 社区上问了一下,被推荐去看看 GO 的后端服务器。
Go 是一种相对较新的语言,由 Google 工程师开发,具有并发特性。
我们将在下一篇文章中学习围棋和多人视频游戏,但首先,我们必须了解并发是如何工作的。
并行执行到底是什么?
与其直接讨论并发性,不如理解什么是并行执行
如果您对计算机科学感兴趣,您可能听说过并行编程,但是如果您没有听说过,请不要担心。
并行执行一个程序基本上就是这个意思。
同时运行两段不同的代码
这意味着两个程序在任何给定的时间点都在运行。一个指令可能会在另一个指令之前完成,但它们不会像常规程序那样顺序执行——一个指令接一个指令。
这很棒,因为这意味着我们可以在相同的时间内将程序的速度提高一倍。同时做两件事比做一件事要快,对吗?
但是伴随着强大的力量而来的是巨大的责任和代价。实际上是两个。
并行运行程序的条件
并行执行需要额外的硬件才能同时运行。
- 每个并行运行的程序需要一个内核
为了并行运行,两个程序必须严格无关。
- 这是因为我们不能在不阻塞其中一个的情况下访问两个程序中的相同数据源,这将使程序不再是并行的
让我们讨论一下限制#1,以便更好地理解为什么我们至少需要两个内核来并行运行。
关于处理器的一些信息
程序和线程由我们计算机的处理器执行。处理器由内核组成,每个内核一次只能处理一条指令。
幸运的是,大多数现代处理器都配有 4 核或更多。这就是为什么我们称它们为四核处理器。像桌子这样的小设备可能只有不到四个内核来节省电池,或者因为它们需要更少的计算能力。
比如我的 2019 款 macbook air 就有双处理器。**平行哭泣* *
因为每个内核一次只能处理一件事情。我们至少需要两个内核才能执行任何类型的并行执行。所以如果你有四个内核,理论上,你可以同时执行四个不同的程序。速度快了 4 倍!但同样,它们必须是严格无关的。
两个相关的程序不能并行运行,因为它们需要某种同步才能正常工作。
双赢的并发性
计算机科学家意识到许多程序不能并行执行,因为它们是相互关联的。
每个内核通常也有 4 个线程。
一个线程只是一个开销较小的程序的子进程。
多线程程序将利用额外的线程和内核来更有效地分配程序负载,而不是让一个糟糕的内核完成所有工作,而其他内核只是旁观。
并发的前提是同时运行两个或更多不同的程序。
利用浪费的 CPU 周期
假设我们有一个程序,它等待用户的输入来打印“Hello {user's input}”,而不是“Hello World”。
一个进程(一个线程也是一个进程)有 5 种状态,在它的生命周期中的任何时候都可以处于这 5 种状态。
由你真正创造
每当程序处于就绪或等待状态时,当程序等待一些输入或事件触发时,cpu 周期就会被浪费。
并发利用这种“浪费的时间”来执行另一个程序,而第一个程序正在等待所述事件,例如等待用户键入他的用户名和密码。
今天的计算机速度如此之快,以至于它们的操作系统使用这种策略来“同时”运行可能的程序,但实际上,程序之间的切换真的很快,以至于我们无法分辨它们的区别。
尽管没有并行那么快,但并发可以显著提高程序的速度。
并发编程很难
既然我们知道了什么是并发性以及它能实现什么,那么让我们来看看在实现它时可能会遇到的一些问题。
比赛条件
当两个进程争夺同一个资源时,就会产生争用情况。这意味着整个程序变得不可预测,因为一个程序可能会在另一个程序读取值之前修改它。
再次举例!
假设我们正在实现一个游戏,并将玩家的位置(X,Y)存储在一个哈希映射中。
现在假设玩家 A 向玩家 B 开枪,我们需要检查子弹是否会击中玩家 B 并造成伤害。
我们游戏的碰撞检测和玩家输入组件是同时运行的,所以我们需要确保不会产生竞争情况。
因此,如果我们有一个函数正在检查玩家 A 的子弹和玩家 B 的角色之间的碰撞,但玩家 B 可能同时在移动,则在我们寻找碰撞时,碰撞检测程序可能会修改玩家 B 在数组中的位置。
在最好的情况下,这会造成游戏逻辑的不一致,就像一个玩家没有受到碰撞的影响,但在最坏的情况下,我们的游戏会崩溃,因为内存不能同时读写。
无论哪种方式,我们都不希望发生这种情况,因为这个问题非常难以调试,因为我们不知道代码何时被执行。
并行问题解决方案
我们将使用下面的多人游戏设计来讨论一些现实世界中可以使用并发的场景。
多人游戏系统设计
线程间共享数据
有时,一些数据需要在线程之间共享,如果处理不当,这可能会导致争用情况。
解决这个问题的一种方法是提供一种同步线程的方法,这样在任何给定的时间,只有一个线程可以访问或读取选定的源。
在围棋中,这些解被称为通道。不要被这个名字吓倒,因为通道基本上只是一个添加了一些功能的队列。
当一个线程从一个通道中读取数据时,它将阻塞对任何其他线程的所有访问,直到当前线程使用完它。如果有两个或更多的线程想要访问通道,它们必须等到通道可用。这样,我们可以确保不会发生竞争情况。
创建数据的副本
我们可能会遇到这样的情况:我们需要每秒更新数据很多次,而与此同时,用户可能同时在修改数据。有时,我们不能对所有事情都使用同一个通道,除非我们不介意所有组件相互依赖。解耦是确保一个易于维护的干净项目的好方法。
比如,让我们回到游戏的例子。当玩家射击时,一个组件负责创建一个射弹,如果它移动了,还负责更新玩家。
使用第一种方法,我们可以通过一个通道发送每一个输入请求和抛射体创建,并让它从一个单一的源更新一切,以防止竞争情况。
但是直接更新玩家的位置并只发送投射物可能是一个更好的解决方案。这样,我们只需要担心更新投射物和检查物理组件中的碰撞,而不是处理玩家的移动。
为此,我们创建玩家位置的副本,并检查与该副本的碰撞,因为原始数据可能会在我们检查它时发生变化。
因为我们每秒 60 次更新抛射体并检查碰撞,这防止了同时读取被修改的数据,也使我们的物理逻辑与玩家的输入逻辑分开。
如上所述,另一种方法是处理物理组件中的所有内容,这会使我们的代码可读性更差,并且依赖于不相关的组件。
下一步是什么?
通过优化资源和等待时间的使用,并发性可以显著加快我们的程序,否则这些资源和等待时间会被浪费掉。在许多情况下,使用并发性可能会改进程序,但它通常是实时应用程序和视频游戏等高性能系统所必需的。
我们将在下一篇文章中讨论用于解决这些问题的各种策略,以及在 GO 中实现并发程序。
与此同时,你可以开始学习围棋,因为它不同于传统语言,可能需要一些时间来适应它。
祝您愉快!
参考
[1]麻省理工学院,并发性(2014),https://web.mit.edu/6.005/www/fa14/classes/17-concurrency/
2伊恩·哈里斯,《围棋中的并发》(2019),【https://www.coursera.org/learn/golang-concurrency
使用 R 中的线性回归模型对条件期望和迭代期望的回顾
重访(意料之外?!)概率论结果使用lm()
太长;没看:
- 您可以对分组变量加上任何其他变量的结果进行回归,未调整和调整后的分组均值将是相同的。
- 我们可以在一个使用
iris
数据的简单示例中看到这一点:
iris %>%
# fit a linear regression for sepal length given sepal width and species
# make a new column containing the fitted values for sepal length
mutate(preds = predict(lm(Sepal.Length ~ Sepal.Width + Species, data = .))) %>%
# compute unadjusted and adjusted group means group_by(Species)
%>% summarise(mean_SL = mean(Sepal.Length), mean_SL_preds = mean(preds)) %>%
kable()
- 这是因为 E[E[Y|X,Z]| Z =Z]= E[Y | Z =Z。
- 我们可以将回归得到的拟合值 E[Y|X,Z]视为随机变量来帮助我们理解这一点。
- 跳到最后看证明。
我承认我在概率论第一学期的大部分时间都在努力理解 X 和 x 之间的区别。当我最终学会了随机变量期望的所有规则时,我仍然对它们在我未来作为一名应用统计学家的工作中的影响毫无兴趣。
最近,当我试图在R
中编写一个看似简单的函数时,我发现自己陷入了期望属性的兔子洞。现在我已经把函数的输出整理好了,我对如何使用回归(一个我非常熟悉的框架)来重新思考我在概率论课程中学到的一些性质有了新的认识。
在函数中,我回归了几个变量加上一个分组变量的结果,然后返回拟合值的分组平均值。我的函数一直输出调整后的组平均值,这些平均值与未调整的组平均值相同。
我很快意识到,对于我需要做的事情,我的分组变量不应该在回归模型中。然而,我仍然感到困惑的是,调整后的和未调整的组均值怎么可能是相同的。
我创建了一个非常基本的例子来测试这个意外的结果。我回归了一个来自iris
数据集的变量Sepal.Length
,一个叫做Sepal.Width
的变量和一个分组变量Species
。然后,我查看了未调整的Sepal.Length
和来自我的线性回归模型的Sepal.Length
的拟合值在Species
的每个类别中的平均值。
library(dplyr)
library(knitr)iris %>%
# fit a linear regression for sepal length given sepal width and species
# make a new column containing the fitted values for sepal length
mutate(preds = predict(lm(Sepal.Length ~ Sepal.Width + Species, data = .))) %>%
# compute unadjusted and adjusted group means
group_by(Species) %>%
summarise(mean_SL = mean(Sepal.Length), mean_SL_preds = mean(preds)) %>%
kable()
我看到了同样奇怪的输出,甚至在我的简单示例中也是如此。我意识到这一定是我学过但后来忘了的一些统计学特性,所以我决定写下我在 expectations 中做了什么。
首先,我以期望值的形式写下了未调整组的均值。我写下了一个条件期望,因为当Species
被限制在某个类别时,我们在看Sepal.Length
的平均值。我们可以通过取一个随机变量萼片长度的期望值,同时设置另一个随机变量物种,一次只等于一个类别,来明确地展示这一点。
E[ 分离 | 物种 = 刚毛
E[ 分离度 | 种类 = 海滨锦鸡儿
E[ 独立 | 物种 = 云芝
更一般地,我们可以使用一个组指示变量物种写出未调整的组均值,它可以采用所有可能的值物种。
E[ 分离 | 种 = 种
这就是我们的未调整群的意思。调整后的群体均值呢?我们可以从写出线性回归模型开始,它是以随机变量 SepalWidth 和物种为条件的 SepalLength 的期望值。
E[ 萼片长度 | 萼片长度,种类
当我对线性回归模型的拟合使用predict
函数时,我从该期望值中获得了拟合值,然后将拟合值分组以获得分组平均值。我们可以将这些拟合值视为随机变量本身,并使用分组指示变量写出另一个条件均值,就像我们之前对未调整的分组均值所做的那样。
E[E[ 分离长度 | 分离宽度,物种|物种 = 物种 ]]
我的未调整和调整的萼片长度的表因此向我显示:
[E[E[sepal length|sepal width,Species|Species=Species
= E[ 分离长度 | 物种 = 物种 ]]
或者,用更一般的符号来说:
E[E[Y|X,Z]|Z=z] = E[Y|Z=z]]
是真的吗?!剧透警报-是的。让我们一个一个地完成证明的步骤。
验证设置
让我们假设证明我们的 Y (结果) X (调整变量)和 Z (分组变量)都是分类(离散)变量。这只是为了使数学更清晰,因为离散变量的期望(加权求和)比连续变量的期望(概率密度函数的积分乘以随机变量的实现)更容易显示。
我们需要一些基本的预期结果:
条件概率
P(A | B)= P(A∩B)P(B)P(A | B)= P(A∩B)P(B)
划分定理
e[a|b]=∑ba⋅p(a=a|b=b)e[a|b]=∑ba⋅p(a=a|b=b)
联合分布的边际分布
∑a∑ba⋅p(a=a,b=b)=∑aa∑b⋅p(a=a,b=b)=∑aa⋅p(a=a)=e[a]
逐步证明
点击每个步骤后的上标数字了解更多信息。
E[E[Y|X,Z]| Z =ZE[E[Y | X,Z]|Z= z
=E[E[Y|X,Z =Z]| Z =Z]= E[E[Y | X,Z =Z]| Z =Z1
=∑XE[Y|X= x ,z =z]⋅p(x=x| z =z)=∑x e[y | x =x,z =z]⋅p(x=x| z =z)2
=∑x∑y⋅p(y=y| x =x,z =z)⋅p(x=x| z =z)=∑x∑y⋅p(y=y| x =x,z =z)⋅p(x=x| z =z3
=∑X∑Y⋅P(Y= y ,X= x ,z =zp(X= x,Z= z )⋅P(X= x ,z =zp(z =z)=∑x∑y⋅p(y=y,x =【t
=∑X∑Y⋅P(Y= y ,X= x ,z =zp(z =z)=∑x∑y⋅p(y=y,X= x ,z =zp(z =z)5
=∑Y∑X⋅P(Y= y ,X= x ,z =zp(z =z)=∑y∑x⋅p(y=y,X= x ,z =zp(z =z)6
=∑Y⋅P(Y= y ,z =zp(z =z)=∑y⋅p(y=y,z =zp(z =z)7
=∑y⋅p(y=yz =z)=∑y⋅p(y=y| z =z)8
= E[Y | Z =Z]= E[Y | Z =Z9
所以,我们已经证明了:
E[E[Y|X,Z]| Z =Z= E[Y | Z =Z]
谢天谢地,这意味着我有了函数输出混乱的答案。我突然意识到,我可以把内心的期望看作一个随机变量,我学到的关于条件和迭代期望的所有规则都可以应用到我每天拟合的回归中。
这里希望你也能感受到不时重温概率论的灵感,即使你的工作非常实用。毕竟,这是一个社交距离的完美活动!😷
说明
- 因为我们使我们的外部期望以 Z= z 为条件,我们也可以将 Z= z 移入我们的内心期望。这在
iris
示例中变得很明显,因为我们仅使用来自Species
的一个类别的拟合值来获得该类别的调整组均值。 ↩ - 我们可以将 E[Y|X,Z= z 改写为 X 可以取的所有可能值的加权求和。E[Y|X,Z= z 将只能取在 x ,E[Y|X= x ,Z= z 范围内变化的 X 值,因为我们的值 z 已经固定了。我们可以用 P(X= x |Z= z )来加权这些可能的 E[Y|X= x ,Z= z 值中的每一个,因为这是 X 在我们已经固定的 z 取值 x 的概率。于是,我们就可以开始用 P(X =X| Z =Z)P(X =X| Z =Z)对每个 E[Y|X=x,Z= z 进行加权,并把它们全部加起来,就可以求出 E[E[Y|X,Z =Z| Z =Z]定理。 ↩
- 我们可以通过与步骤 2 类似的过程(用 P(Y= y |X= x ,Z =ZP(Y =Y| X =X,Z= z )加权每个 y 得到 Y 在 X 的每个可能值上的期望值。 ↩
- 根据条件概率定律,我们可以将条件概率改写为联合分布。 ↩
- 第一个分数的分母与第二个分数的分子相抵消。 ↩
- 我们可以交换求和,这样 y 在 x 的所有值的求和之外。这让我们只得到 y 和 z 的联合分布。 ↩
- 这是一个有条件的期望,写成联合分布的形式。 ↩
- 根据分割定理。 ↩
- 将之前的等式改写为期望值。 ↩
参考
原载于 2020 年 3 月 15 日【https://www.khstats.com】。
理解条件变分自动编码器
由 * 修改而来
(本博客的修改版可以在 这里找到 )
变分自动编码器或 VAE 是一个有向的图形生成模型,它已经获得了很好的结果,并且是生成建模的最先进的方法之一。假设数据是由某个随机过程产生的,涉及一个不可观测的连续随机变量 z. 假设 z 是由某个先验分布 P_θ(z) 产生的,数据是由某个条件分布 P_θ(X|Z) 产生的,其中 X z 有时被称为数据的隐藏表示 X 。
像任何其他自动编码器架构一样,它有一个编码器和一个解码器。编码器部分尝试学习 q_φ(z|x) ,相当于学习数据的隐藏表示 X 或者将 X 编码到隐藏表示中(概率编码器)。解码器部分尝试学习 P_θ(X|z) 对输入空间的隐藏表示进行解码。图形模型可以表示为下图。
(来源)
该模型被训练以最小化目标函数
该损失的第一项是重建误差或数据点的预期负对数似然。通过取几个样本,相对于编码器在表示上的分布来取期望值。当使用来自潜在分布的样本时,该术语鼓励解码器学习重构数据。较大的错误表示解码器无法重建数据。
第二项是编码器分布 q_φ(z|x) 和 p(z) 之间的 Kullback-Leibler 散度。该散度测量当使用 q 来表示优于 z 的先验时丢失了多少信息,并促使其值为高斯值。
在生成期间,来自【N(0,1)】的样本被简单地馈入解码器。训练和生成过程可以表示如下
作为前馈神经网络实现的训练时变分自动编码器,其中 P(X|z)是高斯型的。红色表示不可微分的采样操作。蓝色显示损失计算。(来源)
测试时变化的“自动编码器”,它允许我们生成新的样本。“编码器”路径被简单地丢弃了。(来源)
之所以如此简短地描述 VAE,是因为它不是主要的焦点,但与主题非常相关。
使用 VAE 生成数据的一个问题是,我们无法控制它会生成什么样的数据。例如,如果我们用 MNIST 数据集训练一个 VAE,并尝试通过将 Z ~ N(0,1) 馈入解码器来生成图像,也会产生不同的随机数。如果我们训练得好,图像会很好,但我们无法控制它会产生什么数字。例如,您不能告诉 VAE 产生数字“2”的图像。
为此,我们需要对我们的 VAE 架构做一点小小的改变。假设给定一个输入 Y (图像的标签)我们希望我们的生成模型产生输出 X (图像)。因此,VAE 的过程将修改如下:给定观测值 y,z 从先验分布 P_θ(z|y )中得出,输出 x 从分布 P_θ(x|y,z) 中产生。请注意,对于简单的 VAE,先验是P _θ(z)P _θ(x | z)产生输出。
条件 VAE 中的视觉表征任务(来源)
所以,这里编码器部分尝试学习 q_φ(z|x,y) ,这相当于学习数据的隐藏表示 X 或者将 X 编码成隐藏表示条件 y 。解码器部分尝试学习 P_θ(X|z,y) 对由 y 限定的输入空间的隐藏表示进行解码。图形模型可以表示为下图。
(来源)
条件 VAE (CVAE)的神经网络架构可以表示为下图。
x 是图像。y 是图像的标签,可以是 1 热矢量表示。
CVAE 在 Keras 的实现可在这里获得。
参考资料:
置信区间有什么用?
数据科学,统计学
置信区间是一种显示某些统计数据中的不确定性的方法
来源:作者图片
在本文中,我们将探讨置信区间,并演示如何解释置信区间。
如何表达一个置信区间?
置信区间不是总体均值存在于区间内的概率,但是,它意味着样本存在于阴影区域的概率为 95%。
例如,平均值的点估计不是单一估计,而是置信区间创建平均值的下限和上限。
区间估计值通常很有吸引力,因为总体参数、状态、均值的估计值会随着样本的不同而波动。
区间估计给出了我们对真实均值的估计有多脆弱的迹象。间隔越小,我们的估计就越精确。
例如,如果我们从喜欢产品 A 和产品 B 的顾客中选择第一个 500 人的样本,我们需要推测出喜欢产品 A 的顾客的数量。
我们将以什么方式做这件事?我们将试图通过观察总人口中的样本来发现它。
我们目前从第一个样本中选择的可能性极小,其平均值为 87%。万一我们现在从第二个样本中选择谁更喜欢产品 A,它将不会与平均值完全相同。
现在重复这个动作,再重复一次。毫无疑问,相对于过去的平均值,样本平均值可能是唯一的。那么你如何认识到人口意味着什么?
对此的回应是置信区间。它向你揭示了模糊的人口平均数的最可能的范围。
万一我们为每个样本计算出 95%的置信区间,那么样本的 95%的区间将具有总体均值。
随着样本量的增加,区间范围缩小,样本均值更接近总体均值,因为您正在缩小标准误差等于标准偏差除以样本量的平方根,从而扩大样本量。
因此,它本质上暗示了由于随机抽样误差的存在,我们对给定样本量的样本均值变化的预期程度。
我们以正或负的形式使用置信区间。当利用样本统计评估总体参数时,样本统计肯定是不准确的。始终会有一些误差,其特征是点估计值加上或减去误差幅度。
什么是点估计和区间估计?
照片由 Karina lago 在 Unsplash 上拍摄
置信界限用一个置信系数的文字来表达。尽管置信系数的决定在某种程度上是任意的,但我们通常使用 90%、95%和 99%的区间。
95%的置信区间并不意味着该区间有 95%的可能性包含真实均值。
根据给定样本计算的区间要么包含真实均值,要么不包含真实均值。相反,置信水平与估计区间的技术有关。
置信系数就是给定大小的样本的比例,它可以用来包含真实的平均值。
也就是说,对于 95%的置信区间,如果收集了几个样本并计算了置信区间,则这些区间的大约 95%将具有扩展运行中的真实平均值。
这是点估计和区间估计的根本区别。
如何估计一个总体均值?
照片由 Gursimrat Ganda 在 Unsplash 上拍摄
万一我们热衷于用样本评估总体均值,而总体方差是模糊的,我们假设总体分布是正态的。
用样本方差和(n-1)自由度 t 分布代替总体方差。
在这样的情况下, t 分布看起来,特别像标准的正态分布。主要的对比是它在某种程度上更加分散,并且这种分散的扩展对于小自由度来说更大。
当样本相当大时,自由度是显著的,t 分布和标准正态分布是相同的。
依赖于 t 分布的置信区间很难违反正态性,并且由于中心极限定理,正态总体假设对于较大的样本量不太重要。
例如,我有 95%的信心,新产品的平均客户满意度在 1 到 10 的范围内,从 5 到 7。
什么是比例的置信区间?
调查经常被用来估算比例,因此必须认识到如何为任何人口比例构建一个置信区间。样本需要是自主的。总体应至少是样本的 10 倍。
因此,比例的抽样分布是正态的,这意味着万一你一次又一次地做了类似的调查,并把每个样本的比例画在一条数字线上,图表就会形成一条钟形曲线。
例如,我有 95%的信心,对我们的新产品感到满意的客户的真实比例在 47%到 78%之间。
比例的单侧置信区间从无限大到某个上限,或者从某个下限到无限大。除非样本非常大,否则它们都很宽。
只是利用一个关于双边极限的类似方法,然而对于 95%的置信度,将 5%而不是 2.5%放在尾部。如果假设参数不在区间内,则拒绝应该等同于单侧检验。
在任何情况下,置信区间都是定期估计的,并且这种估计并不是在每种情况下都非常相称。
例如,在二项式中,置信区间通常是利用预期的标准误差来评估的,该误差取决于观察到的比例,尽管假设检验将它与假设的比例放在一起。
例如,对于制造工厂的抽样,找出低于质量的成品比例的单侧 95%置信区间的上限。
在 190 件抽样的成品中,发现 6 件质量不合格。
标准差的置信区间是多少?
在本例中,标准偏差被用作点估计值。在任何情况下,抽样分布都不是对称的,它不是正态分布或 t 分布。抽样分布是一个右偏分布,称为卡方分布。
与 t 分布类似,卡方分布也有一个自由度参数。在此中,单样本置信区间用于测量平均值和标准偏差的 95%置信区间。
均值差异的置信区间是多少?
- 为了达到置信区间,应满足以下假设。这两个群体有一个相似的方差。人口呈正态分布。每个值都是与其他值分开采样的。例如,双样本置信区间方法,发现两个公司客户的平均终身价值之间的差异的置信区间,并查看该置信区间如何帮助锁定最佳客户。
- 在不等方差、中,可以使用稍微不同的方法来计算均值之间差异的置信区间。在这种差异估计中,检验样本量较大的样本多于检验样本量较小的样本。例如,双样本置信区间方法,用于发现机场候机室高峰时段与非高峰时段滞留时间对比的置信区间。
配对样本之间的均值差异是什么?
当要比较的样本以某种规则方式配对时,为了确定配对样本之间的差异,应满足后续假设。它没有合理地剖析两个独立的变量。样本之间应该有关联。
对于样本中的每一对,计算该对的两个分数之间的差值。此时,对这些差异执行单样本分析。例如,双胞胎被要求从 1 到 10 对 iPhone 11 的功能进行评分,并找出他们评分的平均差异。
比例间差异的置信区间是多少?
双样本分析均值间的差异。尽管如此,我们现在不是对比两种方法,而是看比例。例如,找出购买带有和不带有 1 年额外保修优惠券的笔记本电脑的客户比例之间差异的置信区间。
一半的客户包括 1 年额外保修的优惠券。购买带有和不带有 1 年额外保修优惠券的笔记本电脑的客户比例之间差异的置信区间。
如何计算样本量?
- 要计算近似总体均值的的基本样本量,首先确定最大误差,并计算与所需置信区间相关的 z 统计量。例如,在极小的可能性下,你需要 95%确定平均值在你的近似值正负误差范围内。为获得新产品平均客户评级的足够薄的置信区间所需的客户样本量。
- 您还需要估计人口标准偏差。此时的公式是将 z 值乘以标准偏差,然后除以误差,最后将结果平方。向上舍入到最接近的整数。这是在给定置信水平的正负误差范围内逼近总体均值的基本样本量。
- 在任何情况下,你都有可能试图用最小样本量来逼近人口比例。你需要 p,比例的最清晰可及的近似值,结合 z 和误差。计算 z 除以误差,并对结果求平方。乘以 p(1-p)。万一你现在还没有一个可靠的估计值 p,那么将 p 设为 0.5,这将在你解方程时带来最大可能的结果。了解尝试过新产品的顾客比例所需的样本量。
- 用于估计均值间差异的样本量有助于了解每个调查小组中有多少员工必须被抽样到一个充分限定的置信区间,以确定平均投诉数之间的差异。他们有两种类型的员工。管理客户经验较少的人和管理客户经验较多的人。组织需要评估这些员工在平均客户投诉数量方面的差异。组织打算利用相同的样本量,从每种类型的雇员中随机选择样本来获取数据。
- 估计比例差异的样本量了解每个城市必须抽样多少新产品,以实现新产品二次销售比例差异的充分严格的置信区间。销售经理需要知道新产品的二次销售比例在两个城市之间有多大差异。
结论
由 Kelly Sikkema 在 Unsplash 上拍摄的照片
置信区间的问题在于,对于任何一个区间,我们都不能确定它是否包含真实值;然而,我们意识到,它包含真实价值的几率比不包含它的几率要高得多。
它鼓励你适应在产品和服务的不同阶段可能存在的许多不确定性。
现在,把你的想法放在TwitterLinkedin,以及Github!!**
同意 还是 不同意 与绍拉夫·辛拉的观点和例子?想告诉我们你的故事吗?
他对建设性的反馈持开放态度——如果您对此分析有后续想法,请在下面的 评论 或伸出手来!!
推文@ SauravSingla _ 08,评论Saurav _ Singla,还有明星SauravSingla马上!
理解混淆矩阵、精确回忆和 F1 分数
为什么在评估机器学习模型时,准确性不应该是你唯一关心的性能指标
封面图片(图片由 Canva 提供,经作者许可编辑)
当我过去几天在 Kaggle 浏览几本笔记本时,我不禁注意到一些名为“在 dataset_name 上实现 100%的准确性”、“使用 algorithm_name 实现完美的 100%准确性”、的笔记本,以及其他各种关于如何在你遇到的每个数据集上实现 100%准确性的指南。虽然其中一些笔记本在构建数据集的概化模型方面做得很好,并提供了相当好的结果,但大多数笔记本只是对数据进行了过度拟合。
过度拟合是指产生的分析过于紧密或精确地对应于一组特定的数据,因此可能无法拟合额外的数据或可靠地预测未来的观察结果— 维基百科
这一切中最悲伤的部分是什么?他们甚至没有意识到,在试图达到那个黄金数字时,他们对数据集进行了过度拟合。大多数笔记本都是在初学者友好的数据集上找到的,比如“Iris 数据集”或“Titanic 数据集”,这是有意义的,对吗?我们大多数人在开始机器学习的时候只学到了一件事:“准确性很重要”。虽然这是真的,但这只是在一定程度上有关系。这就是为什么我将讨论其他一些性能指标,如混淆矩阵、精确召回和 F1 分数,在评估机器学习模型时,您应该考虑与准确性一起使用。让我们开始吧。
混淆矩阵
在机器学习领域,特别是统计分类问题中,混淆矩阵,也称为误差矩阵,是一种特定的表格布局,允许算法性能的可视化,通常是监督学习算法。— 维基百科
两类分类问题的混淆矩阵(图片来源:作者)
为了理解混淆矩阵,让我们考虑一个两类分类问题,其中两个结果是“肯定的”和“否定的”。给定一个要预测的数据点,模型的结果将是这两个中的任何一个。
如果我们将预测值与真实值(实际值)进行对比,我们会得到一个包含以下代表性元素的矩阵:
真阳性(TP): 这些数据点的实际结果是阳性的,并且算法正确地将其识别为阳性。
真阴性(TN): 这些是实际结果为阴性的数据点,算法正确地将其识别为阴性。
假阳性(FP): 这些数据点的实际结果是阴性的,但算法错误地将其识别为阳性。
假阴性(FN): 这些数据点的实际结果为阳性,但算法错误地将其识别为阴性。
正如您所猜测的,使用混淆矩阵评估模型的目标是最大化 TP 和 TN 的值,最小化 FP 和 FN 的值。
为了更好地理解这个概念,让我们举一个现实生活中的例子:心脏病的预测。在这种情况下,结果将是患者是否患有心脏病。混淆矩阵看起来像这样:
心脏病预测的混淆矩阵(图片来源:作者)
在这里,TP 意味着患者实际上患有心脏病,并且算法预测正确。TN 表示患者没有心脏病,算法预测正确。所以目标应该是尽可能地保持这些值。
混淆矩阵如何帮助检测过度拟合?
为了理解这一点,让我们考虑一下心脏病的例子。大多数时候,当我们处理医疗用例时,很有可能会有一个倾斜的数据集,即一个目标变量会比另一个有更多的数据点。在这种情况下,大多数接受测试的人不会被诊断出任何心脏病。因此,数据集中存在不平衡(偏斜)。
描述倾斜数据集的心脏病示例(图片来源:作者)
如果我们在上图所示的数据集上训练一个模型,由于没有心脏病的患者的数据点数量远远多于有心脏病的患者,该模型将偏向于具有更多数据点的目标(特别是如果我们仅基于其准确性来评估模型)。
现在,经过训练,如果我们在测试集(没有心脏病的患者有 8000 个数据点,而有心脏病的患者只有 2000 个数据点)上测试模型,即使模型预测所有 10000 个数据点都没有任何心脏病,准确率仍然高达 80%。这可能会产生误导,尤其是在假阴性的风险应该可以忽略不计的领域(一个预测患有心脏病的患者与没有心脏病的患者一样的模型将被证明是真正致命的)。
这就是混淆矩阵发挥作用的地方。对于上述场景,混淆矩阵如下所示:
上述场景的混淆矩阵(图片来源:作者)
现在,如果您查看混淆矩阵以及模型获得的准确性,我们可以清楚地识别出模型在训练数据集上过度拟合,因为它将每个未知数据点预测为没有心脏病的患者。如果没有混淆矩阵,我们永远不会知道潜在的问题。
scikit 学习包包括混淆矩阵。你可以在这里查阅官方文档。
精确召回
既然你已经理解了混淆矩阵的作用,那么理解精确召回就更容易了。
我们已经看到准确性在某些情况下是如何误导人的。精确度和召回率帮助我们进一步理解所显示的特定问题的精确度有多高。
精度(也叫正预测值)是相关实例在检索到的实例中所占的比例,而召回(也叫灵敏度)是实际检索到的相关实例总数的比例。因此,精确度和召回率都基于对相关性的理解和度量。— 维基百科
简而言之,精确度意味着所做的正面预测有多少是正确的。
精度公式(图片来源:作者)
在我们的心脏病例子中,它看起来像这样:
精确示例(图片来源:作者)
它可以被翻译成简单的语言,在所有被归类为患有心脏病的患者中,有多少人实际上患有心脏病?
简单来说,回忆意味着分类器正确分类的实际正面预测的百分比。
回忆公式(图片来源:作者)
在我们的示例中,它看起来像这样:
回忆例子(图片来源:作者)
它主要是问,在所有患有心脏病的病人中,有多少人被归类为患有心脏病?
这个公式乍一看似乎有点相同,但是一旦你明白了要点,就很难把两者混淆起来。
scikit 学习包中也提供了 Precision-Recall。你可以在这里查阅官方文档。
精确召回权衡
假设我们训练一个逻辑回归分类器来识别病人是否患有心脏病。如果概率(阈值)大于或等于 0.5,它将预测患者患有心脏病,如果概率小于 0.5,则患者没有心脏病。
现在,如果我们想建立一个模型,只有在对假设非常有信心的情况下,它才能预测患者是否患有心脏病,我们可能需要将阈值提高到 0.7 或 0.8。
在这种情况下,我们最终得到一个具有高精度和低召回率的分类器。精度更高,因为现在分类器更确信患者患有心脏病。召回率较低,因为现在分类器的阈值设置得如此之高,将会有更少的患者被分类为患有心脏病。
另一种选择是,我们建立一个模型,这样它就不会遗漏任何可能的心脏病患者病例(以避免假阴性)。如果一个患有心脏病的病人没有被模型注意到,这可能是致命的。在这种情况下,我们将阈值降低到 0.2 或 0.3,这样即使患者有轻微的可能患有心脏病,也会发出警报,并且可以进行进一步的诊断来证明该假设。
我们这里有一个高召回和低精度的例子。召回率更高,因为我们将对更多的心脏病患者进行分类。精确度较低,因为在大量预测患有心脏病的患者中,一些人在进一步诊断后实际上不会患有心脏病。
一般来说,精确召回值会随着阈值的增加或降低而不断变化。构建具有更高精度或召回率的模型取决于您正在处理的问题陈述及其要求。
f1-分数
精确召回值对于理解特定算法的性能非常有用,也有助于根据需求产生结果。但是当涉及到比较在相同数据上训练的几个算法时,仅仅基于精确召回值就很难理解哪个算法更适合数据。
单纯基于查准率值,哪种算法更好?(图片来源:作者)
因此,需要一种采用精确召回值并提供这些值的标准化表示的度量。
在二进制分类的统计分析中, F1 得分(也称为 F 得分或 F 度量)是一个测试准确度的度量。通过测试的精度和召回计算得出,其中精度是正确识别的阳性结果数除以所有阳性结果数,包括未正确识别的阳性结果,召回是正确识别的阳性结果数除以所有应被识别为阳性的样本数。— 维基百科
F1 得分也可以描述为精确度和召回率的调和平均值或加权平均值。
F1 得分公式(图片来源:作者)
精度或召回值为 0 是不可取的,因此它将使我们的 F1 得分为 0(最低)。另一方面,如果精度和召回值都是 1,那么我们得到的 F1 值为 1,表示完美的精度召回值。F1 分数的所有其他中间值的范围在 0 和 1 之间。
scikit 学习包中也提供了 F1 分数。你可以在这里查阅官方文档。
结论
我希望这篇文章能帮助你理解混淆矩阵、精确回忆和 F1 分数这些术语。使用这些指标肯定会帮助您更好地了解模型的性能。一旦您完全理解了这些概念,您还可以研究一些其他评估指标,如对数损失、ROC-AUC 曲线、分类交叉熵等等。
参考
- 使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习:构建智能系统的概念、工具和技术(非附属)
- 精确度和召回率的权衡—【吴恩达】
- 混淆矩阵—维基百科
- 精确召回——维基百科
- F1 得分—维基百科
理解共轭先验
基于 PyMC3 的贝叶斯机器学习方法
贝叶斯机器学习:传统方式!(图片鸣谢:Saptashwa!)
在这篇文章中,我将全面介绍共轭先验的概念,包括一些例子。共轭先验的概念非常有用,因为共轭先验将贝叶斯更新简化为仅修改先验分布中的一些参数。他们也有必要从贝叶斯方法中学习和理解机器学习。这篇文章使用的 Jupyter 笔记本可以在我的 GitHub 中找到(下面的链接)。你可能期望从这篇文章中学到什么—
- 共轭先验的定义
- 为什么相关?
- 简单的例子来理解这个概念。
- 使用 PyMC3 作为解决一般贝叶斯推理的工具。
因此,让我们毫不迟疑地开始吧:
定义:
贝叶斯公式:图像信用:作者
在贝叶斯概率理论中,如果后验分布与先验分布同族,那么先验和后验称为共轭分布,先验称为似然函数的共轭先验。最初,你可能认为这很酷,但是这对我们有什么帮助呢?
为什么共轭先验?
在贝叶斯方法中,我们的目标是获得后验分布。在现实生活问题中,证据的计算可能会带来痛苦。比方说,我们正在处理图像,我们的目标是从一组给定的图像中生成一个新的图像。这将是非常困难的建模图像的分布,这是我们的 P(D) 。此外,我们可以将 P(D) 视为一个归一化常数,它可以写成— ∫ P(D|θ) P(θ) dθ 。想象一下在神经网络环境中的这个积分,其中 θ s 是参数,即网络的权重,如果它是一个相对较深的网络,将有数百万个 θ s。因此积分将是难以处理的。共轭优先是来拯救我们从这种痛苦。如果我们的先验分布(在上面的例子中,它将是权重的分布)是似然函数的共轭先验,我们可以获得精确的后验分布。我们来看一个恶作剧的例子:
因为我的朋友 Kitty 有一只不太友好的猫 Nuo,拍拍她会导致要么以概率 p 变得暴躁要么以概率 (1-p)开始咕噜咕噜叫。由于我对这种行为不太了解,我假设一个关于 p 的先验分布,带有一个 Beta 分布 , Beta(2,2) 。如果诺在一个晚上发了 6 次脾气,只咕噜了 2 次,那么 p 的后验分布的参数是什么?每一次拍诺都可以被认为是两个固定结果之一的单个实验。这实际上被称为伯努利试验,类似于抛硬币。由于贝塔分布与伯努利似然共轭,我们已经可以知道后验分布将遵循贝塔分布(如下面的推导所示)。让我们试着找到参数…
正如我们所见,对于伯努利可能性,N1次的结果为 1,而第次的结果为 0。选择具有参数 a,b 的贝塔先验,给出具有参数 (N1 + a,N0+b) 作为后验的贝塔分布。因此,选择共轭先验有助于我们仅通过更新先验分布的参数来计算后验分布,并且我们根本不需要关心证据。鉴于我们的问题,我们将有一个后验分布,这将是一个参数(6+2,2+2)的贝塔分布。一旦我们知道了贝塔分布的参数,我们就可以计算出 x = 0.7 时的最大后验概率,如下图所示。此外,请注意,一旦我们使用了可能性,初始先验(参数为 2,2 的 Beta 分布)如何变为更新后的后验。这是贝叶斯统计中的关键思想,即在看到数据后,我们对模型的最初信念是如何改变的!有关共轭分布的详细列表,请点击查看。**
不同参数下贝塔分布的概率分布函数。对于上面的例子,β先验被选择为具有参数(2,2),在观察一些伯努利试验之后,该参数被更新,并且我们获得了具有参数(8,4)的后验分布。来源:作者
贝叶斯更新的简单例子:二项式和贝塔
二项式分布给出了从 N 次伯努利试验中准确获得 k 次成功的离散概率分布(其中每次伯努利试验的结果以概率 p 为真,以概率 q= 1-p 为假)。
让我们用一个更现实的例子来扩展前面的愚蠢例子,我们将利用二项分布与贝塔分布共轭的事实。对于单次试验(例如:掷硬币),二项分布等价于伯努利分布。随着我们收集更多的数据,我们将看到我们的后验知识是如何更新的。让我们假设我们使用一个有偏向的硬币,其中伯努利试验的成功概率(正面)是 0.4。我们认为新的观察结果是二项分布,其中 k 头来自 N 次试验。从简单的β先验— β(1,1)(相当于均匀先验)开始,在下图中,我们看到更多的数据如何帮助我们更新和减少后验分布的不确定性。
用增加的伯努利试验(掷硬币)更新贝塔后验概率,成功概率= 0.4。用 python 和赛璐珞创作。来源:作者
让我们看看我用来制作这个简单动画的部分代码——
首先,我们使用scipy.stats.bernoulli.rvs
创建伯努利试验的 numpy 数组,然后计算成功(人头)和失败的数量。基于这个数字,我们从统一的先验开始更新贝塔分布。赛璐珞模块用于创建 matplotlib 动画。
PyMC3 用于贝叶斯建模:
PyMC3 是构建在the no之上的用于贝叶斯统计建模的 Python 包。它允许用户使用包括马尔可夫链蒙特卡罗(MCMC)在内的各种数值方法来拟合贝叶斯模型。让我们从在 PyMC3 中构建硬币翻转模型开始。
这里,我们从使用伯努利似然开始,使用参数(1,1)的 Beta 先验。在伯努利函数的observed
变量中,我们将输入数据。这是一种告诉 PyMC3 我们希望根据已知情况(观察结果)来处理未知情况的方式。
我们创建伯努利试验,这将是我们的输入数据。给定这个观察到的数据,然后我们告诉 PyMC3 生成 15,000 个样本,我们返回生成的样本和观察到的数据。这样,我们就可以调用函数并生成样本了—
我们创建一个观察值列表(伯努利试验),循环收集所有生成的样本和相应的观察值,并将它们存储在一个字典中。执行此代码块时,您将看到使用不掉头采样器(螺母)生成的样本数为 62,000。PyMC3 中的默认采样器是 NUTS,它并行使用 4 个内核(最大数量的 CPU)来创建 15000 个样本。最初的 2000 个样本(每个岩心中 500 个样本)用于调整坚果取样器,稍后将最终丢弃。查看 文档 了解更多。一旦样本生成过程完成,我们就可以想象随着试验(观察)次数的增加,后验曲线的演变
后验分布的演化。使用[PyMC3 traceplot](https://docs.pymc.io/api/plots.html). Source: Author
绘制
我们可以清楚地看到,随着试验次数的增加,后验分布的不确定性越来越小。让我们绘制生成样本的直方图,并与真实的 beta 分布(使用 Scipy 计算)进行比较
使用 PyMC3 的采样后验分布(左)与使用 Scipy 获得的真实β分布(右)的比较。来源:作者
在这里,我们已经讨论了共轭先验,并通过一些简单的例子来巩固我们对它的重要性的理解。希望这至少能帮助你开始贝叶斯机器学习。
保持坚强,干杯!
页(page 的缩写)s:我在另一篇文章中介绍了贝叶斯机器学习中另一个可能是最重要的概念,即潜在变量和期望最大化算法。
参考:
[1] 共轭先验的注释【M. Jordan 教授;加州大学伯克利分校。
【2】链接到我的笔记本!
为什么梯度下降适用于线性回归
凸优化和机器学习之间的联系
阿萨·罗杰在 Unsplash 上的照片
当我第一次开始自学机器学习时,我惊讶地发现我已经有了一些这方面的背景。我在大学里只上过一门计算机科学入门课,所以我期望有更高的学习曲线。我过去的线性代数课以及 3Blue1Brown 线性代数系列(强烈推荐)都派上了用场。我没想到会派上用场的是我的优化课。这可能是因为我在对机器学习一无所知之前就上了这门课,因此很快就忘记了课堂上提到的这两者之间的任何明显关系。但当我开始学习线性回归时,我仍然记得看到“梯度下降”这几个字时,我想,“等等,我已经知道这是什么了。”凸性理论使我们很容易理解为什么我们可以使用梯度下降来解决线性回归成本函数。在这篇文章中,我希望涵盖该理论的基础,并提供对线性回归的更深入的理解。
凸面
我们先通过凸集和凸函数来定义凸性。史蒂芬·博伊德的凸优化教科书是这个主题的一个很好的资源。凸集定义如下:
直观地说,在二维空间中,我们可以把凸集想象成一个形状,在这个形状中,无论你画什么线来连接集合中的两点,该线的任何部分都不会在集合之外。
(左)凸集,(中)非凸集,(右)凸集
凸集的这种定义正好符合如下所示的凸函数的定义:
正如凸优化教科书中所述,你可以直观地将凸函数想象为这样一个函数,如果你从(x,f(x))到(y,f(y))画一条线,那么凸函数的图形将在这条线的下方。下面是三个例子,我们用这种直觉来判断函数是否是凸的。
(左)具有唯一优化器的凸函数,(中)非凸函数,(右)具有多个优化器的凸函数
我们可以看到中间的图不是凸的,因为当我们画一条连接图上两点的线段时,有一些点(x,f(x))f(x)大于线段上相应的点。
左边和右边的图形都是凸的。无论你在这些图上画什么样的线段,该线段总是在函数图的上方或等于函数图。右边的图有许多极小值,而左边的图有一个唯一的极小值。唯一的极小值意味着只有一个点上的函数最小。由于左边的图形是一个二次函数,通过对函数求导可以很容易地找到唯一的极小值。
现在我们对凸集和凸函数有了一些直觉和理解,让我们转向线性回归,看看凸性在哪里起作用。
线性回归概述
线性回归的目的是使最佳线性模型适合一组数据。假设有 m 个数据样本在 n 维空间中。每个样本都有 n 个映射到单个输出值的特征。我们可以访问输入和输出数据,但是我们想弄清楚输入数据和输出数据之间是否存在线性关系。这就是线性回归模型的用武之地。这个模型被写成这样的形式:
现在,我们确定最佳线性模型的方法是求解模型的系数,使我们的估计输出值和实际输出值之间的误差最小化。我们可以使用线性最小二乘法来实现这一点。因此,我们的成本函数如下:
我们称这个函数为“成本”函数,因为我们计算的是估计值和真实值之间的总误差或成本。由于线性最小二乘问题是一个二次函数,我们可以解析地最小化这个成本函数。但是,对于大型数据集,使用称为梯度下降的迭代方法来查找最佳系数通常计算速度更快。如何使用梯度下降来最小化成本函数的分解如下所示:
成本函数的凸性
现在让我们进入一些凸优化理论。如上所示,梯度下降被应用于寻找成本函数的全局最小值。但是我们怎么知道全局最小值的存在呢?当最小化一个函数时,凸函数确保如果存在最小值,它将是全局最小值。我们前面看到,二次函数是凸函数。既然我们知道线性最小二乘问题是一个二次函数,我们也知道它是一个凸函数。
此外,像线性最小二乘问题这样的二次函数是强凸的。这意味着该函数具有唯一最小值,且该最小值是全局最小值。因此,当我们应用梯度下降算法时,我们可以确信它将收敛于正确的极小值。如果我们试图最小化的函数是非凸的,如上图所示,梯度下降可能会收敛于局部最小值而不是全局最小值。这就是为什么非凸会更难处理的原因。这很重要,因为许多机器学习模型,最著名的是神经网络,是非凸的。下面你可以看到一个最简单的梯度下降法找不到全局极小点的例子。
非凸函数上收敛到局部最小值的梯度下降示例
结论
优化是机器学习的核心。虽然对二次函数应用梯度下降可能看起来很直观,但看看凸分析理论如何证明这种直观是很有趣的。这个理论可以扩展到分析任何机器学习模型,以了解它可能有多难解决。随着我继续我的机器学习之旅,我很兴奋地了解到优化可以帮助阐明不同机器学习概念的其他方式。
通过在 Julia 中实现来理解卷积
一个关于我在 Julia 中实现卷积的实验以及从中得到的启示的故事。
罗马法师在 Unsplash 上拍摄的照片
在 Fast.ai 深度学习课程的第二部分中,我了解到不仅要能够使用 Tensorflow / PyTorch 等深度学习库,而且要真正理解其背后的想法和实际发生的事情,这很重要。没有比我们自己尝试和实现它更好的理解它的方法了。
在机器学习实践中,卷积是我们都非常熟悉的东西。所以我想,为什么不试一试呢?下面是我在 Julia 中实现卷积的实验结果。
为什么是朱莉娅?
当我上吴恩达的机器学习课程时,我主要是用 MATLAB 来理解机器学习算法。但是 MATLAB 是一种商业产品,可能非常昂贵。
Julia 非常快,并且专门设计为非常擅长数值和科学计算,这是我们在实现机器学习算法时需要的。也很好学。最重要的是,它是免费和开源的。
与其他语言相比。来源
开始实验
好了,现在是开始工作的时候了。你看,已经有这么多关于 CNN 的文章了,所以我就不多解释了。如果你正在研究 CNN 的基本概念,我推荐吴恩达的这个 Youtube 播放列表。
从简单的事情开始总是好的。所以对于我的实验,我将使用 6x6 矩阵作为输入,3x3 矩阵作为滤波器,以及 4x4 矩阵的预期输出。我从小矩阵开始,这样我可以检查我的函数的准确性。
通过电子表格卷积。
我通过 Julia CLI 定义输入、过滤器和预期输出。输入将是 6x6 矩阵,滤波器将是 3x3 矩阵,预期输出将是 4x4 矩阵,具有与上图相同的值。
**julia>** input = [[3,2,4,2,7,6] [8,0,2,1,7,8] [2,2,10,4,1,9] [1,5,4,6,5,0] [5,4,1,7,5,6] [5,0,2,7,6,8]]6×6 Array{Int64,2}:
3 8 2 1 5 5
2 0 2 5 4 0
4 2 10 4 1 2
2 1 4 6 7 7
7 7 1 5 5 6
6 8 9 0 6 8**julia>** filter = [[1,1,1] [0,0,0] [-1,-1,-1]]3×3 Array{Int64,2}:
1 0 -1
1 0 -1
1 0 -1**julia>** output = [[-5,-8,-2,1] [0,-12,-5,5] [4,4,2,-4] [3,6,0,-10]]4×4 Array{Int64,2}:
-5 0 4 3
-8 -12 4 6
-2 -5 2 0
1 5 -4 -10
迭代#1
因为我们已经知道输入(6x6)和滤波器(3x3),所以输出应该是 4x4。在第一次迭代中,如果我们手动进行计算,并假设过滤器为 3x3,代码将如下所示:
第一次迭代
我们可以在 CLI 中复制整个函数并查看结果。
**julia>** conv_1(input, filter) == outputtrue
而且很管用!Julia 的另一个很棒的特性是我们可以对我们的函数进行基准测试。我们可以通过使用基准工具来完成。
**julia>** using BenchmarkTools**julia>** @benchmark conv_1(input, filter)BenchmarkTools.Trial:
memory estimate: 208 bytes
allocs estimate: 1
--------------
minimum time: 196.880 ns (0.00% GC)
median time: 200.165 ns (0.00% GC)
mean time: 212.749 ns (0.82% GC)
maximum time: 1.828 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 616
正如你所看到的,朱莉娅速度惊人!但是如果我们有超过 3x3 的滤镜呢?
迭代#2
在第二次迭代中,让我们通过遍历一个滤波器矩阵来尝试这样做。代码将如下所示:
第二次迭代
**julia>** conv_2(input, filter) == outputtrue
代码正在工作,现在它可以接受任何大小的过滤器。它的性能怎么样?
**julia>** @benchmark conv_2(input, filter)BenchmarkTools.Trial:
memory estimate: 208 bytes
allocs estimate: 1
--------------
minimum time: 485.372 ns (0.00% GC)
median time: 488.586 ns (0.00% GC)
mean time: 517.087 ns (0.33% GC)
maximum time: 13.017 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 191
还是很快的!但是我能让它更快吗?也许如果我找到另一种方式,而不是通过过滤器循环,这将使它更快,更简洁地阅读。
迭代#3
在 Julia 中,我们可以用点语法做到这一点。因此,我们可以这样做,而不是遍历过滤器:
第三次迭代。
第三次迭代现在更加简洁。让我们试一试:
**julia>** conv_3(input, filter) == outputtrue**julia>** @benchmark conv_3(input, filter)BenchmarkTools.Trial:
memory estimate: 5.20 KiB
allocs estimate: 33
--------------
minimum time: 2.378 μs (0.00% GC)
median time: 2.482 μs (0.00% GC)
mean time: 2.679 μs (2.05% GC)
maximum time: 71.501 μs (95.64% GC)
--------------
samples: 10000
evals/sample: 9
令我惊讶的是,它不但没有变快,反而变得更慢了,而且差别很大!!这是为什么呢?文档是这样写的:
在其他语言中,为了提高性能,通常也需要矢量化:如果循环很慢,函数的“矢量化”版本可以调用用低级语言编写的快速库代码。在 Julia 中,矢量化函数不是性能所需的而不是,事实上,编写自己的循环通常是有益的。
所以第二次迭代是目前为止卷积的最佳实现。但是这太简单了。如果我更进一步呢?
带衬垫
卷积中常用的另一个概念是填充。如果我们有 6×6 输入矩阵和 3×3 滤波器矩阵,而不是 4×4 矩阵作为输出,通过填充,我们可以得到 6×6 矩阵。这种方法通常被称为“相同”卷积。如果你想了解更多关于填充的概念,请尝试观看来自吴恩达的视频 C4W1L04 。
如何在我们的卷积函数中实现填充?一种方法是重新创建输入矩阵,在真实值周围填充。计算我们需要多少衬垫的公式是:
padding = (filter - 1) / 2
上面的公式假设滤波器矩阵的大小是奇数。即使不总是,这也是很奇怪的。所以在我的实现中,我假设滤波器矩阵的大小是奇数,我将用 0 填充填充。
带衬垫
当我试图计算填充值时,您可能会注意到\运算符。这是因为 Julia 的类型稳定性特征。基本上所有的/
运算符都会返回 Float,所有的\都会返回 Integer。这种类型稳定性是 Julia 超快的一个主要原因!
让我们试试这个函数,以获得与输入和滤波器矩阵“相同”的卷积。请注意,从[2,2]到[5,5]的值与前一个函数完成的“有效”卷积相同。
**julia>** padding_conv_1(input, filter, "same")6×6 Array{Float64,2}:
-8.0 1.0 2.0 -5.0 1.0 9.0
-10.0 -5.0 0.0 4.0 3.0 10.0
-3.0 -8.0 -12.0 4.0 6.0 12.0
-10.0 -2.0 -5.0 2.0 0.0 13.0
-16.0 1.0 5.0 -4.0 -10.0 18.0
-15.0 3.0 10.0 -1.0 -9.0 11.0**julia>** padding_conv_1(input, filter, "same")[2:5,2:5] == conv_2(input, filter)true
我们的 padding_conv 函数的性能也不差。
**julia>** @benchmark padding_conv_1(input, filter, "same")BenchmarkTools.Trial:
memory estimate: 992 bytes
allocs estimate: 2
--------------
minimum time: 1.746 μs (0.00% GC)
median time: 1.803 μs (0.00% GC)
mean time: 1.865 μs (0.72% GC)
maximum time: 70.076 μs (96.21% GC)
--------------
samples: 10000
evals/sample: 10
虽然与预期的“有效”卷积相比相当慢,但内存分配仍然很低。
步进卷积
同样,我想通过尝试实现“步进”卷积来改进我的卷积。通常情况下,步幅=1。如果你想了解更多的概念,请观看来自吴恩达的视频 C4W1L05 。
实现步进卷积有点棘手。首先,我需要根据输入、过滤器和步幅找到输出矩阵的大小。该尺寸的公式为:
result = (input-filter) ÷ stride + 1
因此,如果我们有 7×7 输入矩阵和 3×3 滤波器矩阵,并且跨距=2,我们将有 3×3(而不是跨距=1 的 5×5)。棘手的部分是迭代输入矩阵。在我们的简单卷积中,输入和输出矩阵之间的变化率是相同的,即 1。这就是为什么我们在迭代输入矩阵时可以使用变量i
和j
。
但是当步幅不为 1 时,我们需要一个额外的变量来迭代输入矩阵。代码如下:
迈着大步
让我们看看性能结果。在这次检查中,我使用 7x7 输入矩阵和 3x3 滤波器。这是为了让我可以尝试 stride=1(预期 5x5 过滤器)和 stride=2 (3x3 过滤器)。
**julia>** input7 = rand(7,7)**julia>** @benchmark stride_conv_1(input7, filter)BenchmarkTools.Trial:
memory estimate: 288 bytes
allocs estimate: 1
--------------
minimum time: 742.395 ns (0.00% GC)
median time: 747.081 ns (0.00% GC)
mean time: 772.902 ns (0.40% GC)
maximum time: 5.709 μs (86.29% GC)
--------------
samples: 10000
evals/sample: 124**julia>** @benchmark stride_conv_1(input7, filter, 2)BenchmarkTools.Trial:
memory estimate: 160 bytes
allocs estimate: 1
--------------
minimum time: 319.876 ns (0.00% GC)
median time: 322.438 ns (0.00% GC)
mean time: 327.930 ns (0.56% GC)
maximum time: 2.753 μs (87.78% GC)
--------------
samples: 10000
evals/sample: 233
步幅越大,速度越快!这是有意义的,因为我们比以前迭代得少。
把所有的放在一起
我们已经看到了如何使用填充和步幅> 1 进行简单卷积。让我们看看能否将它们放在一个函数中。
基本二维卷积
我们可以在 CLI 上尝试一下。
**julia>** @benchmark conv2d(input7, filter, 2, "same")BenchmarkTools.Trial:
memory estimate: 944 bytes
allocs estimate: 2
--------------
minimum time: 980.100 ns (0.00% GC)
median time: 1.044 μs (0.00% GC)
mean time: 1.072 μs (1.00% GC)
maximum time: 55.501 μs (97.63% GC)
--------------
samples: 10000
evals/sample: 10
结论
我所做的只是一个非常基本的二维卷积算法。在实践中,我们通常使用多层的三维卷积,并且通常与池算法配对。但对我来说,在 Julia 中学习并实现一个非常基础的卷积已经是非常令人兴奋的事情了。
我也还在学习朱莉娅。请随时告诉我如何改进我的代码。感谢阅读。
如果你对代码感兴趣,这里有 GitHub 上资源库的 链接。
理解神经网络中的卷积和汇集:一个简单的解释
对使卷积神经网络工作的概念及其背后的直觉的直观解释
来源: Unsplash
卷积网络的工作原理?它们背后的魔力是什么,让我们在图像分类、物体检测、人脸识别等广泛的应用中取得成功?
事实证明,所有这一切都是可能的,这要归功于两个惊人简单但强大的概念:卷积和汇集。
在这篇文章中,我将尝试用一种真正直观和视觉的方式来解释它们,把数学放在后面。我在网上找了很多有很强数学基础的文档,但是我觉得核心思想有时候被所有的公式和计算冲淡了。
卷积:这都是关于过滤器的
我们需要理解的第一个关键概念是卷积运算。这很简单:我们将把一个滤镜应用到一个图像上来得到一个结果图像。
例如,假设我们有一个输入图像和一个过滤器:
来源:自己的阐述
请记住,任何图像都可以表示为像素矩阵,每个像素代表一种颜色强度:
卷积的作用如下:
这是,我们正在建立* 另一个图像通过应用过滤器到我们的输入图像。请注意,根据我们应用的过滤器(其形状和值),我们将获得不同的图像。*
但是,我们为什么要这样做呢?似乎没有太大意义!
事实是,这确实很有道理。为了理解为什么,让我们得到一个黑白网格的简单图像。
来源:自己的阐述
(请注意,这已经不是矩阵了。它实际上是一个图像)
并对其应用一些过滤器:
来源:自己的阐述
我们可以看到,使用特定的滤波器,输出图像只包含垂直边缘。让我们再试试:
来源:自己的阐述
在这种情况下,我们只得到输出图像中的水平线。另一种不同的过滤器允许我们强调边缘,而不管方向如何:
来源:自己的阐述
显然,我们可以应用一个滤波器,使输入图像保持不变:
来源:自己的阐述
这是过滤器背后的想法,现在你可能更好地理解为什么它们被这样称呼了:它们允许我们保留图片中的某种信息,而 T2 忽略其他信息。这是可能的,因为卷积运算是如何工作的。
但是,让我们用另一个图像来看看关于过滤器的更有趣的事情:
来源:自己的阐述
如果仔细观察结果,您会看到垂直线被删除,但水平线被保留。但是一个有趣的细节是,因为这个图像是等轴透视的,所以有而不是一条单独的纯水平线。
然而,卷积能够处理透视图并且显示实际上是水平的线,尽管透视图并没有这样显示它们。
最后,让我们得到一个真实世界的图像,并应用一些过滤器,看看我们得到了什么。为了便于解释,我们将得到一张黑白照片,但对于彩色图像,直觉几乎是一样的。
让我们应用一些过滤器,看看会发生什么:
来源:自己的阐述
来源:自己的阐述
正如我们所预料的,根据我们应用的过滤器,我们只从图像中提取我们想要的信息。
现在我们已经了解了卷积的工作原理,让我们引入另一个让它更加强大的想法:池化。
池化:增强卷积的能力
池的概念很简单:
来源:自己的阐述
也就是说,我们将获取像素组(例如,2x2 像素组)并对它们执行聚合。我们可以进行的一种可能的聚合是取组中像素的最大值(这被称为最大池化)。另一种常见的聚合是取平均值(平均池)。
但是,这有意义吗?要回答这个问题,让我们得到一个以前的图像,并应用一个 2x2 最大池:
来源:自己的阐述
我们可以看到两件事:
- 首先,图像尺寸缩小了到一半:通过采用 2x2 像素的组并只保留最大值,现在图像变大了一半。
- 我们在使用垂直边缘滤波器进行卷积时保留的边缘不仅被保留,而且还被增强。
这意味着我们已经能够减少图像包含的信息(通过仅保留一半的像素),但仍然保留并增强了滤波器在卷积图像时显示的有用特征。
这如何适应卷积神经网络?
最后,我将提供一个直观的解释,说明这两个概念是如何构建在卷积神经网络中的。同样,我不会进入数学细节,因为这超出了本文的目的。
一个基本的卷积神经网络可以被视为一系列的卷积层和池层。当图像通过它们时,重要的特征被保存在卷积层中,并且由于汇集层,这些特征被加强并通过网络保存,而丢弃所有对任务没有影响的信息。
当我们在神经网络中旅行时,这些重要的特征可以从单线或边(如我们在示例中看到的)到更复杂的东西(例如,狗的耳朵)。
还有一个细微但至关重要的细节:在我们看到的例子中应用卷积时,我们选择了一个过滤器,然后观察输出。实际上,例如,如果我们正在执行图像分类任务,网络将学习哪些过滤器允许我们提取最有洞察力的特征,以便区分我们训练数据集中的类别。
你自己试试吧
我已经创建了一个笔记本,这样你就可以自己尝试所有这些想法。您可以上传您想要的图像,并尝试不同滤镜和池的效果。
笔记本可以在这里找到。
我建议在 google colab 上运行它,这样你就不必在电脑上安装所有必要的依赖项。
读完这篇文章后,我希望你学会了关于卷积和池如何工作以及为什么它们在 CNN 中有用的直觉。在下一篇文章中,我们将更详细地介绍图像如何穿过 CNN 的各个层,以及如何在每个步骤中提取特征。
免责声明 :本帖提供了关于卷积神经网络如何工作的直觉。为了便于解释,我省略了一些概念,如处理彩色图像中的不同通道、步幅、填充等。这些概念对于全面理解 CNN 很重要,所以一定要牢记在心!此外,这篇帖子的灵感来自 deeplearning.ai 的tensor flow in Practicespecialization,对 CNN 背后的直觉做了极好的解释。**
理解相关性和多样化
安德鲁·雷德利在 Unsplash 上的照片
为什么投资不相关的资产是值得的
相关性是金融学和统计学中的一个基本概念。简单地说,相关性告诉我们两个变量一起移动的可能性。高相关性意味着当一个变量上升时,另一个也很可能上升。类似的公司,如可口可乐和百事可乐,它们的股票回报是正相关的:
正相关的事物往往会一起运动
负相关意味着相反(当一个变量上升时,另一个变量通常下降)。股票和国债往往是负相关的。
负相关的事物往往会向相反的方向运动
相关性为零相当于统计独立性。如果两个变量在统计上是独立的,这意味着两者互不影响。如果你和你最好的朋友每人扔一个六面骰子,结果将是独立的(不管你们两个有多少共同的想法)。
不相关的事物彼此独立运动
方差和不确定性
是时候做一点统计了。变量的方差是衡量它变化多少的尺度。你可以通过想象一个游戏来描绘变化,在这个游戏中,你把一根羽毛丢在地上,并试图预测它会落在哪里。如果那天没有风(或者你在室内玩),那就相当容易了。如果没有阵风把它吹来吹去,我们知道羽毛不会飞得太远。因此,我们可以说羽毛的最终潜在着陆点具有低方差:
在没有风的情况下,我们可以合理地确定羽毛会落在哪里(低方差)
如果你碰巧在龙卷风中掉了你的羽毛,那么谁也不知道羽毛会落在哪里(它可能会落在几英里以外或更远的地方)。潜在着陆点的极大范围就是高方差的一个例子:
在大风的情况下,我们不知道羽毛会落在哪里(高方差)
另一种思考方式是,低方差意味着低不确定性,高方差意味着高不确定性。因此,在可能的情况下,在金融、数据科学和一般生活中,我们希望减少方差,这是有道理的。更少的差异意味着结果更可预测,计划不太可能出错,我们不太可能承受损失(无论是经济上的还是情感上的)。因此,我们可以在保险、应急计划和对冲上花费更少的时间和资源。
低相关性多样化
这就是相关性(或缺乏相关性)的原因。假设我们有两个正态分布的随机变量,X 和 y。这两个变量的均值都是 0,标准差是 1,相互之间的相关性是 0。关于正态分布的基础知识,请看这篇博文。
我们两个随机变量平均值的标准差是多少?我们需要引入一些数学规则来解决这个问题:
- 方差是标准差的平方(因此在我们的例子中,X 和 Y 的方差都是 1,因为 1 = 1):
Var(X) = Stdev(X)
- 当我们将正态分布的随机变量 X 乘以常数 W 时,方差与常数的平方成比例:
Var(W*X) = W *Var(X)
- 两个随机变量 X 和 Y 之和的方差等于它们的方差之和,如果 X 和 Y 是独立的:
Var(X+Y) = Var(X) + Var(Y)
取平均值就像对每个随机变量应用一个常数 0.5,然后求和。因此,我们可以将上述两条规则结合起来,计算出平均值的方差,如下所示:
Var(0.5 * X+0.5 * Y)= 0.5 * Var(X)+0.5 * Var(Y)
酷,现在我们可以计算 X 和 Y 的平均值的方差和标准差(让我们使用实际数字— Var(X)=1 和 Var(Y)=1):
var(0.5 * X+0.5 * Y)= 0.5 * 1+0.5 * 1 = 0.25+0.25 = 0.5
为了简化我们的符号,让我们将 X 和 Y 的平均值称为 A。我们可以很容易地计算 A 的标准偏差,因为我们已经知道它的方差:
stdev(A)= Sqrt(Var(A))= Sqrt(0.5)= 0.71
等等发生什么了?x 和 Y 的标准偏差都是 1,但它们的平均值的标准偏差是 0.71——变量的平均值比单个变量本身的波动性小。我想在这里强调两个关键:
- 取平均值很像创建投资组合。这里,我们将 X 和 Y 分别乘以 0.5,取其平均值。但是我们可以很容易地称 X 为苹果股票回报,Y 为谷歌股票回报。那么我们会把 0.5 理解为一个权重。X 和 Y 的平均值就是平均分配给苹果和谷歌股票的投资组合的回报。因此,当我们进行投资组合时,我们实际上是在对他们的个人回报进行加权平均。
- X 和 Y 的平均值的方差(和标准差)低于它们各自的方差。发生这种情况是因为它们彼此独立运动,这使多样化(一个之字形,而另一个之字形,因此群体的平均运动比任何一个个体的运动更不稳定)。另一种理解方式是大数定律。您进行的独立观察越多,这些观察的平均值就越有可能接近真实值(因此 100 次观察的平均值比仅仅 3 次观察的平均值具有更低的方差)。
重要的是要注意,这种影响只发生在变量的相关性小于 1(理想情况下远小于 1)的时候。如果两件事完全相关,那么我们不能指望它们的运动多样化——相反,它们会以完全一致的步伐一起运动(即使我们平均了数千个完全相关的变量,方差也不会减少)。
还要记住,两个不相关变量的平均值的方差并不总是低于变量的单个方差。当一个变量具有高方差而另一个具有低方差时,两个变量的平均值的方差将介于两个个体方差之间。但是由于多样化,平均值的实际方差将明显小于单个变量方差的算术平均值。
我们看到,当我们对两个独立变量取平均值时,标准差是如何从 1 下降到 0.71 的。如果我们可以获得更多不相关的变量来增加我们的投资组合,会怎么样?下图显示了当我们增加更多不相关的随机变量时,标准差会发生什么变化:
添加更多不相关变量对平均值标准差的影响
最初增加的几个变量导致标准差在收益递减开始前急剧下降。这是意料之中的——我们已经包含在平均值中的变量越多,我们就越不应该预期每个新变量会对事物产生影响。但即便如此,我们可以看到大数定律在起作用——我们平均的变量越多,方差越低,我们就越有把握。转到金融领域,我们能找到的不相关投资越多,我们投资组合回报的方差(和标准差)就越低,我们就越确定我们不会亏钱。要了解为什么金融行业使用投资组合的标准差作为其风险的代理,请查看我以前的博客。
这正是想要击败世界的对冲基金想要做的。他们试图找到尽可能多的不相关的回报流,将它们组合成一个投资组合,然后希望印钱。通常情况下,事情不会像计划的那样发展,但这是一个美好的梦想。
实际问题
好了,现在我们都兴致勃勃地寻找尽可能多的不相关投资来填充我们的投资组合,是时候给我们的游行浇点冷水了:
- 真正不相关的投资真的很难找到。你希望你的投资不仅现在是不相关的,而且在经济危机中也是不相关的(当你真正需要多样化的好处时)——而这些更难找到。事实上,大多数投资至少在某种程度上是相关的,这意味着将它们结合起来仍然会减少方差,只是没有那么多。从数学上来说,两项投资的相关性越低,它们就越多样化(见这里的等式)。下图显示了改变两个正态分布随机变量之间的相关性如何改变其平均值的标准偏差:
更高的相关性意味着更少的多样化(更少的方差减少)
- 几乎每个人都拥有的典型风险投资是股票。因此,找到一种与股票负相关的投资将是一件好事,它让我们能够对冲风险(并降低我们整体投资组合的方差)。不幸的是,每个人都想这样。因此,任何与股票回报呈负相关的投资,比如国债、看跌期权或 VIX 期货,都会被定价为溢价,可能会给你带来低回报或负回报(嘿,保险从来都不是免费的)。国债很有趣,因为在过去几十年里,它们的回报与股票的回报呈负相关,而且它们的预期回报为正(不像其他对冲基金,你必须直接支付)。因此,股票和债券的核心配置是大多数长期投资组合的关键。然而,这种负相关并不是必然的——在很多年的时间里(比如 20 世纪 70 年代),股票和债券的回报高度相关(利率的持续上升通常会损害股票和债券)。
- 这就引出了我的最后一点——我们想要正的投资组合回报(我们想要随机变量的平均值尽可能地大于零)。这意味着零或负预期收益的不相关随机变量对我们没有用。是的,将它们加入我们的投资组合会降低总体方差,但也会降低预期回报(加权平均),这将适得其反。
这篇文章是我投资组合优化入门的第三部分。如果你还没有看过,也看看第 1 和第 2 部分:
了解交叉验证
萨姆·巴耶在 Unsplash 上拍摄的照片
交叉验证如何帮助我们避免过度拟合的陷阱
我记得我建立的第一个量化模型。这是一个基于我研究的各种市场和经济因素,在美国股票和新兴市场股票之间战术性转移资金的模型。
我花了大量的时间设计(实际上是过度设计)我的功能并运行回溯测试,直到我确信我有一个世界无敌的产品。我的模型最后投产的时候,花了一年时间,基本上什么都没做。最终,我认为它产生了轻微的负累积回报。
我认为自己很幸运。回想起来,我意识到我的模型严重高估了,我很幸运,它推荐的赌注没有让我的公司损失惨重。
识别模型何时过度拟合
今天我们将讨论交叉验证,这是一种帮助我们估计模型样本外性能并防止过度拟合的技术。但首先,让我们谈一谈什么是过度拟合,为什么会发生。
过度拟合是指我们对现有数据的训练过度,以至于模型失去了泛化能力。泛化能力好的模型能够合理成功地适应新数据,尤其是那些与该模型到目前为止看到的任何观察结果都不同的数据。因此,如果一个过度拟合的模型不能概括,那么当它投入生产并真正脱离样本时,它很可能表现不稳定(可能很糟糕)。过度拟合的最常见原因是:
- 伪相关性:如果我们足够努力地寻找,我们会发现强相关性。例如,我们可能会发现比特币的价格与津巴布韦的披萨价格高度相关。但这最有可能是由于偶然性和随机性,而不是任何真实的东西,我们将钱押在这样的相关性上是愚蠢的。如果我们用一堆与我们试图预测的事物虚假相关的因素来拟合我们的模型,它将不能很好地概括。
- 过度使用测试集:这个真的很难完全避免。如果模型在我们的测试集(测试集是我们提供的数据的一部分,以便我们可以评估模型如何在新数据上推广)上工作得不好,那么我们将调整它,直到我们找到在训练集和测试集上都工作得好的配置。这样做的含义是,测试集不再是对我们模型的样本外性能的无偏估计——毕竟,一旦我们开始使用保留集(也称为测试集)做出建模决策,那么我们真的可以认为它仍然是保留的吗?
- 有偏差的训练集:我们的训练数据很少能真正代表我们试图建模的人群。因此,我们应该意识到,我们实际上肯定会遇到我们的模型在某些时候发现完全不熟悉的数据。虽然我们应该尽最大努力使样本的特征与总体特征相匹配,但我们也应该知道样本的不足之处。因为这些领域的数据对我们的模型来说是最大的风险。如果我们的样本只代表一小部分人口,那么随着时间的推移,我们的模型将表现不佳。
交叉验证有什么帮助
交叉验证是一种技术,它允许我们使用训练集产生测试集,如评分指标。也就是说,它允许我们仅使用我们的训练数据来模拟“超出样本”的影响,因此我们可以了解我们的模型概括得有多好。
如果没有交叉验证,传统的模型训练过程如下所示:
传统列车测试分离
我们在蓝色部分进行训练,直到我们觉得我们的模型已经准备好面对野外。然后我们在测试集上给它打分(黄金部分)。传统方法的缺点是我们只有一次机会。当我们在测试集上测试我们的模型时,我们已经损害了我们的测试数据。如果我们的测试结果很糟糕,那怎么办?我们真的可以放弃所有这些小时的工作吗?或者我们只是开始为测试集优化我们的结果?
要是有一种方法可以模拟我们的模型可能在测试集上的表现,而不实际使用测试集就好了。有!这叫做交叉验证。
交互效度分析
我们将关注一种特定类型的交叉验证,称为 K-folds(所以当我仅仅说交叉验证时,我指的是 K-folds 交叉验证)。K 折叠交叉验证将我们的训练数据分成 K 个折叠(折叠=子部分)。然后,我们训练和测试我们的模型 K 次,以便每个折叠都有机会成为伪测试集,我们称之为验证集。让我们使用一些视觉效果来更好地理解正在发生的事情:
三重交叉验证
假设我们正在开发一个预测线性回归模型,并使用 R 作为我们的主要评分标准。我们已经将一些数据分成了训练集和测试集。我们主要关心的是我们的模型的样本外预测的准确性(它概括得有多好)。因此,我们决定直到最后才查看测试集(这样我们可以给我们的模型一个智力上诚实的分数)。
但是当我们调整和改进我们的模型时,我们仍然想知道我们所做的改变会如何影响它的样本外性能。所以我们交叉验证:
- 我们决定运行 3 重交叉验证,这意味着我们将训练数据分成大小相等的 3 重。
- 在我们的交叉验证的运行 1 中,折叠 1 被保持(标记为保持 1 的粉红色矩形)。因此,我们在不在折叠 1 中的训练数据(蓝色矩形)上进行训练,然后在折叠 1 上进行验证。这意味着我们使用非折叠 1 训练数据来拟合我们的模型,然后计算并记录我们对折叠 1 中因变量的观察值的预测程度。至关重要的是,在运行 1 中,我们在模型训练期间没有使用折叠 1 中的任何数据,因此使用折叠 1 计算的 R 有点像样本外的 R。
- 在运行 2 中,折叠 2 伸出。现在,Fold 1,以前是我们的验证集,已经成为我们的训练集的一部分。我们使用折叠 1 和折叠 3 中的数据来拟合我们的模型,并使用折叠 2(通过 R)对其进行评分。
- 运行 3 结束后,我们现在有三个 R 值(因为每一次折叠都有一次机会)。三个 R 的平均值给了我们模型中样本外 R 的合理估计。
关于交叉验证的工作原理,需要记住的最重要的一点是,在每次运行中,它报告的得分指标都是根据给出的倍数计算的。
结论
使用交叉验证时的一些提示:
- 重要的是要记住,我们的模型的交叉验证分数(比如 R)最多是对其在测试集上的性能的乐观估计。就像测试集上的性能充其量是对模型的真实概括能力的乐观估计。
- 如果我们对模型所做的更改增加了训练分数(这是在样本中估计的),但降低了交叉验证分数,那么这是一个好迹象,表明我们过度拟合了模型。
- 折叠太少会束缚模型,这是因为在每次交叉验证运行中,有太多的训练数据被保留。例如,对于 2 次折叠,一半的训练数据被保留,这意味着该模型仅适合剩余的一半(导致得分度量低于它们应有的值)。一个不适合的模型和一个过度适合的模型一样糟糕。
- 在我们训练测试分割之前打乱数据是一个好的做法,以防数据被排序。如果它以某种方式进行了排序,而我们忽略了对它进行洗牌,那么我们的训练测试分割将提供有偏差的数据集,其中任何一个都不能很好地代表实际人群。
- 一旦我们通过交叉验证完成了模型的改进,我们应该在测试集上测试它之前,在整个训练集上重新调整模型。
更多数据科学相关帖子由我:
用 OpenCV、OCR 和 DNNs 理解纵横字谜
你能教机器如何阅读填字游戏吗?
https://unsplash.com/photos/zlbB-anyO3I
简介
最近,我被分配了一个任务,创建一个算法,从纵横字谜照片中提取所有可能的元数据。这对我来说似乎是个有趣的任务,所以我决定试一试。以下是这篇博客将要涉及的话题:
- 基于 OpenCV 的纵横细胞检测与提取
- Pytorch CNN 纵横字谜细胞分类
- 单元元数据提取
你可以在我的网站和我的 Github 上找到完整的代码实现。
纵横字谜细胞检测
首先,要提取元数据,您必须了解它的位置。为此,我使用简单的 OpenCV 试探法来识别纵横字谜上的线条,并用这些线条形成一个单元格网格。输入图像需要足够大,以便可以容易地检测到所有的线。
同一图像上的输入图像和输出行
之后,对于细胞检测,我找到了线之间的交叉点,并根据交叉点形成了细胞。
图像线交点
最后,在这一阶段,每个细胞都从图像中切下,并保存为一个单独的文件,以供进一步操作。
从单个图像中提取的细胞
用 PyTorch CNN 进行纵横字谜细胞分类
对于细胞分类,一切都很简单。该问题被建模为具有以下目标的多类分类问题:
{0: 'both', 1: 'double_text', 2: 'down', 3: 'inverse_arrow', 4: 'other', 5: 'right', 6: 'single_text'}
对于每个目标类,我为每个类手工标记了大约 100 个单元格。之后,我用以下架构安装了一个简单的 PyTorch CNN 模型:
class Net(nn.Module):
# Pytorch CNN model class
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 3)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 3)
self.conv3 = nn.Conv2d(16, 32, 5)
self.conv4 = nn.Conv2d(32, 64, 5)
self.dropout = nn.Dropout(0.3)
self.fc1 = nn.Linear(64*11*11, 512)
self.bnorm1 = nn.BatchNorm1d(512)
self.fc2 = nn.Linear(512, 128)
self.bnorm2 = nn.BatchNorm1d(128)
self.fc3 = nn.Linear(128, 64)
self.bnorm3 = nn.BatchNorm1d(64)
self.fc4 = nn.Linear(64, 7)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(F.relu(self.conv2(x)))
x = F.relu(self.conv3(x))
x = self.pool(F.relu(self.conv4(x)))
x = x.view(-1, 64*11*11)
x = self.dropout(x)
x = F.relu(self.bnorm1(self.fc1(x)))
x = F.relu(self.bnorm2(self.fc2(x)))
x = F.relu(self.bnorm3(self.fc3(x)))
x = self.fc4(x)
return x
最终的模型预测几乎是下降的,甚至在不同格式的填字游戏中也能很好地概括。
单元元数据提取
我的最后一步是从标记的单元格中提取所有元数据。为此,我首先以 Pandas DataFrame 格式创建了每个图像单元的分类表示。
图像单元文本表示
最后,基于 cell 类,我或者使用 Pytesseract 从图像中提取文本,或者如果单元格被归类为箭头单元格之一,则提取箭头坐标和方向。
该脚本的结果输出以 JSON 格式显示如下:
{“definitions”:
[{“label”: “F Faitune |”, “position”: [0, 2], “solution”:{“startPosition”: [0, 3], “direction”: “down”}},
{“label”: “anceur”, “position”: [0, 4], “solution”: {“startPosition”: [1, 4], “direction”: “down”}}]
}
结论
这项工作对我来说是一次很好的经历,并提供了一个很好的机会来深入研究一项混合了简单的 OpenCV 试探法以及使用更前沿的概念(如 OCR 和 DNNs)进行图像分类的任务。谢谢你的阅读!
你可以在我的 网站 上查看其他帖子
理解计算机视觉:人工智能如何看待我们的世界
研究人员是如何设法让计算机理解它们看到的东西的?让我们来找出…
"在所有的感觉中,视觉肯定是最令人愉快的."
——海伦·凯勒
人工智能和神经网络如此受欢迎的一个原因可能是费李非教授在斯坦福大学开设的视觉识别公开课。与安德鲁·恩盖(Andrew Ngai)等许多其他有影响的人一起,他们吸引了大量对深度学习的关注。无论是检测医学图像中的癌症组织,还是在自拍照上添加可爱的动物耳朵,还是将汽车开上街道, C 计算机 V 视觉( CV )为我们描绘了一个美好的未来,在这个未来中,机器可以在许多方面让我们的生活更加便利。
机器人看着手,图像来自 Quantica
在本文中,让我们先来了解一些与 CV 相关的历史。然后,我们将进入一些基本的 CV 技术,如直线和边缘检测。在我们建立了基础和直觉之后,我们将进入现代 CV 模型如何工作的细节。在简要介绍深度学习之后,我们将更详细地了解卷积神经网络的基础知识。最后,我们将回顾一些当今最先进的算法。
计算机视觉简史
"如果我们想让机器思考,我们需要教它们看东西."
—费·
从人工智能转向,CV 领域的研究开始于 20 世纪 60 年代左右。最早的相关论文之一可能是 Hubel 和 Wiesel 在 1959 年发表的猫的纹状皮层中单个神经元的感受野,他们将电极插入猫的大脑,并在改变猫的视觉后观察反应。
他们在 1962 年发表了另一篇论文,对猫的大脑如何处理视觉信息进行了更详细的研究。神经科学相关方法的早期研究相当糟糕。我在之前的一篇文章中提到过,基于神经网络的方法经历了一段黑暗时期,直到 2000 年。
Hubel & Wiesel 的实验,图片来自 CS231n 幻灯片
在这个领域还有其他的进展。例如,霍夫变换(Hough's transform)是以 1962 年保罗·豪格的专利霍夫变换命名的,现在广泛应用于自动驾驶等领域。边缘检测是由 John Canny 在 1986 年提出的,在边缘检测中使用得相当多。边缘检测应用于许多不同的领域,如人脸识别。
当我在加拿大皇后大学(Elon Musk 也去了那里)学习时,格林斯潘教授也展示了他们在机器的对象持久性方面的进展。加拿大人在人工智能研究领域做出了很多贡献,包括著名的 CIFAR。
在我们进入深度学习之前,另一个值得一提的是 Lenna Forsén。如果你在任何与数字图像处理相关的领域,你很可能在某个地方见过她的照片。1960 年,劳伦斯·罗伯茨是第一个在他的硕士论文中使用她的照片的人。
这张照片后来不知何故成了图像处理领域的标准测试图像。《花花公子》杂志计划就肖像权提起诉讼,但当他们意识到这是为了研究和教育时,就好心地放弃了。
Lenna Forsén 的肖像,图片来自维基百科
现在让我们回到神经网络。受 Hubel 和 Wiesel 工作的启发,Kunihiko Fukushima 在 1980 年发明了“新克隆”。Neocognitron 可以说是卷积神经网络的最早版本。网络被用来识别手写字符。
Yann LeCun 因其在卷积神经网络方面的工作而闻名,他于 1989 年将反向传播应用于卷积神经网络。然后他在 1998 年发表了基于梯度的学习算法 LeNet-5 。
神经网络的转折点发生在 2012 年。Alex Krizhevsky 和他的 AlexNet 在 9 月 30 日赢得了 ImageNet 竞赛,展示了基于卷积神经网络的方法的优越性能。
ImageNet 的首席科学家和首席研究员费教授于 2006 年开始研究 ImageNet 的想法。她继续在世界各地的会议和会谈中旅行,影响其他人进入计算机视觉领域。
另一个值得一提的可能是约瑟夫·雷德蒙。雷德蒙在 2016 年发明了 YOLO 篮网。YOLO 这个词可能来自一个流行的网络俚语“YouOonlyLiveOnce”,暗示人们要充分享受生活,通常用在年轻人将要做出鲁莽举动的时候。在论文中,YOLO 代表“你只看一次”,这提供了基于神经网络的快速对象检测。
Redmon 在他的简历中也认为自己是一匹小马,指的是“我的小马”,这是一个针对小女孩的美国特许经营。
计算机视觉的进步正以许多不同的方式让我们的世界变得更美好。是时候让我们了解这些算法是如何工作的了!
计算机视觉:基础,在神经网络之前
自从有了电视,监视器屏幕就通过调节三种不同颜色的Ca nodeRyTubes(CRT)的亮度来显示图像——Red(R)、Ggreen(G)和 B lue ( B 尽管现在我们的屏幕可能使用了更先进的硬件,如液晶显示器(LCD)或有机显示器(T42),图像通常以 RGB 表格的形式存储和传输例如,位图将图像存储在范围从 0x000000 到 0xFFFFFF 的十六进制值的数组中,这些值可以表示范围从 0 到 255 的 3 个数字。
位图插图,来自媒体上的it 下一篇文章
由于压缩格式或其他约定,某些图像格式可能会以不同的方式存储图像,但它们通常可以转换回 RGB 值数组。在数学直觉的背景下,RGB 值的差异并不代表它们在人类感知方面的实际差异。 C 委员会 I 国际deL’éclairage(CIE)提出了δE度量,从人类感知的角度更准确地表示了色差。****
CIE 1931 色彩空间中的麦克亚当图,图片来自维基百科
计算机如何存储和处理图形信息是另一个巨大的领域,我们可以在另一篇文章中深入探讨。
边缘检测和特征提取
“视觉图像是由我们大脑以边缘的形式将像素组打包在一起的能力构建的。”
—吉尔·博尔特·泰勒
由于像素阵列可能很复杂且有噪声,因此很难垂直分析图像。这也是研究人员通常提取边缘、线条等特征的原因。这些特征可以用简单得多的数值或关系来表示。有许多方法来检测边缘,如采取导数或 Canny 的方法。在本文中,我们将简要回顾一下 Sobel 的方法。
不同边缘检测算法之间的比较,图像来自 PyImageSearch
边缘本质上是像素间的高度差异,因此大多数边缘检测方法试图提取检测到像素间差异的区域。Sobel 的方法通过用两个特殊的核对图像的 3×3 区域进行卷积(由操作符 ***** 指示)来检测边缘。核将产生梯度的水平和垂直分量,其可用于计算表示边缘的方向和强度的向量。
索贝尔方法演示,作者图片
关于数学的更多细节可以在维基百科上找到,并在下面的视频中解释。作者还制作了其他图像处理算法的视频,如 Canny 边缘检测器和模糊过滤器。
寻找边缘(Sobel 算子),视频来自 Youtube
霍夫变换与自动驾驶
“在苏俄,汽车载着你!”
—俄罗斯逆转
想象一下,我们是一家汽车公司的工程师,该公司希望发明无人驾驶汽车。在某些时候,我们将不得不教汽车在道路上的车道内行驶。否则,我们的车会像《一家人》里的亚洲女司机一样。
来自家庭的亚洲女司机
如果交通线是连续的,那就容易了。我们可以确保当赛车太靠近线的任何一侧时,赛车会向后移动一点。但是我们知道,在大多数地方,比如加利福尼亚,街道上都有虚线。如果汽车只检测到它接近线,它可能会在实线之间的间隙失控。
经过处理的虚线图像,来自媒体的图像文章
由于霍夫变换,我们将能够从虚线重建直线。霍夫变换将 x-y 空间内的直线转换为 m-c 空间内的点。然而,x-y 空间内的点将被转换成 m-c 空间内的线。m-c 空间还具有这样的性质,即所有与同一点相交的线将对应于 x-y 空间内同一直线上的点。关于 Hough 变换直线检测的更多内容,这里是一篇论文。
霍夫变换演示,图片作者
霍夫变换不仅能检测直线,还能检测圆。阅读圆形霍夫变换和广义霍夫变换了解更多信息!
走向深度学习:卷积神经网络和残差神经网络
“没有人告诉一个孩子如何看,尤其是在早年。他们通过真实世界的经历和例子来学习这一点。”
—费·
“机器学习”这个短语最早是由亚瑟·塞缪尔在 1952 年提出的。然而,在 2000 年之前,神经网络的研究一直受到严重阻碍。人工神经元的最早版本是由神经生理学家沃伦·麦卡洛克和数学家沃尔特·皮茨在 1943 年发明的。它旨在描述神经元如何在我们的大脑中工作。
在 2012 年 ImageNet 大赛之后,网络公司( CNN )成为了明星。从那以后,我们开始在任何可以与之相关的领域中超过 80%的新发表的论文中看到神经网络相关的模型。还有许多其他的机器学习算法,如 k-NN 和随机森林,但在图像处理方面,CNN 的性能不如它们。
深度卷积神经网络
每当提到卷积神经网络,数据科学家首先想到的可能是 Yann LeCun,他在 1998 年发表了关于 LeNet-5 的论文。
LeNet-5 的架构,图片来自原文
典型地,CNN 由 4 种类型的层构成。我在我的 Alpha Go 文章中提到了 3 个,因为它们是更常见的 3 个。研究人员也可能在他们的论文中加入一些小的变化,并发明新的图层类型。这 4 层是卷积层、汇集层、有限线性层和全连接层。
1。卷积层
卷积层(3x3),图片作者
卷积层通常作为卷积神经网络的第一层出现。这些类型的图层将使用过滤器扫描源图层,并将总和放入目标图层。有些过滤器擅长检测边缘,有些则擅长其他任务。卷积过程可以提取 2D 图像内部的空间信息,并将其传递给下一层。关于不同种类的卷积滤波器及其在计算机视觉中的应用的更多细节可以在这里找到。
2。汇集层
池层(2x2 最大池),作者图片
池层遍历源层,并在有界区域内选择一个特定的值。该值通常是该区域内的最大值、最小值和平均值。将信息缩小也称为“下采样”或“二次采样”。
当计算资源有限时,与将像素直接输入多层感知器的节点相比,通过卷积层和池层预处理的网络更加资源友好。在特殊情况下,“上采样”技术也可以用来产生更多的数据。
热路层
热卢层,作者图片
ReLU layer 将值提供给一个 ReLU 函数,该函数只是去掉负值。ReLU 是神经网络中常用的激活函数,因为它可以降低消失梯度问题的可能性。
全连接层
全连接层只是一个人工神经网络,图片作者
一个完全连接的层本质上是一个多层感知器,它有时被称为“softmax”,本质上做一些被称为“加权和”的事情。我已经在我的 Alpha Go 文章中解释了更多关于多层感知器的内容,包括前馈和反向传播。
深度残差神经网络
2015 年,微软研究院的何和他的团队发表了他们的论文用于图像识别的深度残差学习。论文将残差学习应用于卷积神经网络,取得了比当时所有其他State-of-the-Art(SOTA)模型更好的性能。
剩余连接示例,图片来自原始论文
剩余学习的一个关键概念是“捷径连接”的使用。这涉及到在网络的各层之间添加一个连接,这样信息就可以通过跳过层来传递。在对人脑的分析中也发现了这样的结构。具有跳过权重的网络也称为高速公路网络。
ResNet 与其他型号的对比,图片来自原纸
该网络还在 CIFAR-10 数据集上实现了更好的性能。
今日计算机视觉:语义分割、目标检测和图像分类
“大哥看着你呢!”
— 1984,乔治·奥威尔
随着计算机视觉的进步,人工智能现在可以做许多疯狂的事情。出于对隐私侵犯的担忧,欧盟甚至考虑禁止面部识别。而其他国家,如中国,已经将面部识别嵌入他们的社会信用系统。
凡事都有取舍。随着电子支付和街头摄像头的进步,我可以向你保证,上海街头的扒手比过去少多了。与洛杉矶相比,上海的犯罪活动也少得多。
图片分类工作流程,图片作者
当我们谈论计算机视觉时,我们脑海中浮现的可能是包围盒中的标签图像。首先通过区域提议过程由区域勾勒出对象的轮廓,然后将检测区域内的内容。有 3 个热门的研究领域:语义分割,对象检测和图像分类。
语义分割:CASENet 和 Mask R-CNN
语义分割与边界和边缘的检测高度相关。例如,CASENet 勾勒出物体的边界。
使用 CASENet 进行语义分割,演示来自 Youtube
Mask R-CNN 可能是一个更受欢迎的模型,以语义分割著称。网络从 R-CNN 发展到快速 R-CNN 再到更快的 R-CNN 。
带掩码的语义分割 R-CNN,来自 Youtube 的演示
对象检测:YOLOv1、YOLOv2 和 YOLOv3
“你只看一次!”
—约瑟夫·雷德蒙
由 Joseph Redmon 于 2016 年发布的 YOLOv1 已经在物体检测方面展示了比当时其他模型快得多的速度。作者将该型号逐步改进为性能更好的约洛夫 2 和约洛夫 3 。
YOLOv2、YOLOv3、Mask R-CNN 和 Deeplab Xception 之间的比较,演示来自 Youtube
图像分类:CNN 的图像网络现状和史诗般的失败
ImageNet 邀请世界各地的研究人员在他们的数据集上竞争,最先进的模型仍在快速迭代。也许你会定义下一个 SOTA?
来自论文的影像分类精度可视化,代码,2020
CNN 上还有其他有趣的事实。一组研究人员在 2019 年 ICLR 上发表了一篇论文,显示 CNN 像人类一样基于纹理而不是形状对图像进行分类。
神经网络根据纹理、来自原始纸张的图像做出判断
神经网络也容易受到高频干扰。最近一篇关于太极的论文显示,当图片上有波纹时,VGG-16 会把松鼠归类为金鱼。
VGG-16 认为有波纹的松鼠是金鱼,图片来自原文
伊恩·古德费勒很久以前就研究过针对神经网络的对抗性攻击,当我们学习生成模型时,我们会更深入地研究他。
话说到最后…
“AI 无处不在。这在未来并不是什么可怕的事情。艾和我们在一起。”
—费·
我终于按计划完成了这篇文章。这个领域有太多的东西要写,你不可能在 10 分钟的文章里写完。我希望这篇文章能引起你对其中一些的兴趣。我可能会在未来的文章中写关于手势识别和社会行为预测的内容。
CV 和 NLP 是人工智能领域的两个热门话题。如今,云人工智能平台正把大部分精力投入到这两个领域,因为它们有潜在的应用价值。我也写过一篇关于 NLP 的文章。我计划稍后写自动化机器学习、生成模型和云平台。关注我了解更多信息。
此外,我还有一篇关于计算机视觉前端框架的文章。这些框架很容易自己尝试,也许今晚你可以用它们做一些很酷的应用程序!
逐步了解数据分析
对数据集应用数据分析以获得关于数据的见解。
马库斯·斯皮斯克在 Unsplash 上的照片
在当今世界,每天都有超过 2.5 万亿字节的数据被生成。成为一名数据科学工程师/数据科学家/ML 工程师或任何你可能称之为数据分析艺术的人是你应该掌握的第一件事。
什么是数据分析?
这是一个对数据进行检查、清理、转换和建模的过程,以便我们可以从数据中获取一些有用的信息,并将其用于未来的预测。
数据分析工具使用户更容易处理和操作数据,分析数据集之间的关系和相关性,它还有助于确定模式和趋势以进行解释。
在这里,我们将使用 python 来深入研究数据分析,并研究数据分析的所有重要方面。在这里,我们将使用一个汽车设计数据集,你可以在这里下载。
从导入重要的库和读取数据集开始
#importing the important libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns#reading the dataset and creating a dataframe
df = pd.DataFrame(pd.read_csv('car_design.csv'))
df.head()
正如我们在这里看到的,数据被加载到名为“df”的数据帧中,因此让我们从数据争论部分开始,如了解数据的维度,数据有多少行和列。
步骤 1 理解数据
# Dimention of dataset
print("Dataset is of ", df.ndim, " dimension.")# Rows and column of dataset
print("Dataset has ", df.shape[0], " rows.","\nDataset has ", df.shape[1], " columns.")#Knowing the data Types of the columns
print("Data Types :")
print(df.dtypes)
上述所有步骤将帮助我们理解数据及其属性。
步骤 2 清理数据
当我们浏览数据时,我们看到一些列包含“?”而不是被认为是数据异常的正确数据。我们需要将它替换为“NaN ”,以执行进一步的操作。
df = df.replace("?", np.nan)
df.head()
这里你可以看到吗?替换为 NaN
现在,我们将用平均值的列替换缺失的数据,即 NaN,此操作只能在数据类型为 float 的列上执行。因此,我们将转换所选的列,将它们转换为 float,并用平均值替换缺失的数据。
# Replacing the missing data with respective column meanfor i in ['normalized-losses', 'bore', 'stroke', 'horsepower', 'peak-rpm', 'price']:
df[i] = df[i].fillna(round(df[i].dropna().astype('float64').mean(),2))
步骤 3-了解数据的统计摘要
现在数据已经清理完毕,没有任何异常,让我们向前看一下数据的统计描述。为此,我们将使用描述功能。描述函数计算与数据框列相关的统计数据摘要。
df.describe()
数据框的所有数字属性的统计摘要。
现在让我们进入下一步。
步骤 4 数据可视化
在此,我们将根据问题陈述尝试可视化不同的列,并找出不同属性之间的关系(如果有的话)。因为我们这样做是为了通用目的,所以我们将可视化我们选择的列。
# Plotting histograms of the following numerical attributes -- # "engine-size", "peak-rpm","horsepower","price"hist_col_names = ["engine-size", "peak-rpm","horsepower","price"]
for i in hist_col_names:
df[i] = df[i].astype(float) # It changes data typeplt.figure(figsize=(15,15))
cnt=1
for i in hist_col_names:
plt.subplot(4,2,cnt)
sns.distplot(df[i], hist=True, color = 'darkblue', kde_kws={'linewidth': 4})
plt.title(i)
cnt=cnt+1
plt.tight_layout()
plt.show()
选定数值属性的分布图。
只要有一点统计学知识,我们就能清楚地理解这些分布图想要表达的意思。在此之后,我们还可以找到不同数字属性的相关性,并使用热图将它们可视化。
热图是数据的二维图形表示,矩阵中包含的各个值用颜色表示。
# Creating the correaltion matrix
corr = df.corr()# Plotting the correlation matrix on a heatmap
fig, ax = plt.subplots(figsize=(12,9))
sns.heatmap(corr, annot=True)
ax.set_xticklabels(ax.get_xticklabels(), rotation=45);
plt.show()
描述不同数值属性之间从正到负的关系的热图。
另一种真正有洞察力的数据可视化图称为箱线图,这是一种通过四分位数图形化描述数字数据组的方法。箱线图也可以有从方框延伸的线,表示上下四分位数之外的可变性。
让我们在数据集的“驱动轮”和“价格”属性之间绘制一个箱线图。这将有助于我们找出不同驱动轮汽车的价格范围。
plt.figure(figsize=(8,8))
sns.boxplot(x="drive-wheels", y="price", data=df,linewidth=1.5, palette ='Set2')
在这里,我们可以清楚地看到不同驱动轮类别的价格范围。
现在让我们进入最后一步,从数据中获得一些见解。为此,让我们设想我们的问题陈述为“我们需要找出一家新公司推出的不同类型汽车的价格范围?”
步骤 5:从数据中得出推论/见解。
为了解决这个问题,让我们先根据驱动轮找出汽车的平均价格。
df_group=df[['drive-wheels', 'body-style', 'price']]
drive_wheel_avg=df_group.groupby(['drive-wheels'], as_index=False).mean()
drive_wheel_avg
现在,我们将根据不同车型的车身风格显示驱动轮的平均值。
group_avg=df_group.groupby(['drive-wheels','body-style'] , as_index=False).mean()group_avg
最后一步是将上述步骤中获得的数据转换为数据透视表,以便更好地可视化、精确查看和理解。
pivot_table=group_avg.pivot(index='drive-wheels', columns='body-style')
pivot_table=pivot_table.fillna('Not Applicable')
pivot_table
该表根据车身风格和驱动轮清楚地描述了不同汽车的价格范围。
该表可以帮助我们根据已经上市的其他公司的数据,找到我们可以推出汽车的价格段。这里我们可以清楚地看到,这种推断只是基于有限的属性,如果我们想要得到更精确的推断,我们需要考虑更多的属性。
在本文中,我向您解释了在处理数据集时应该遵循的基本步骤。遵循这些步骤没有硬性规定,但它们有助于你获得更好的洞察力。
请在我的 github 中找到数据集和完成的 python 文件,在那里我考虑了更多的属性,你可以通过它们更好地理解。
Streamlit 是一个很棒的工具,可以轻松构建视觉上吸引人的仪表板。
towardsdatascience.com](/creating-streamlit-dashboard-from-scratch-59316a74fa1) [## 使用 Faker 创建数据集并将其用于熊猫概况分析
创建您自己的数据并对其执行操作。
towardsdatascience.com](/creating-dataset-using-faker-and-use-it-for-pandas-profiling-6fe26e1b9557)
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。 您也可以在我的Github中查看我在这里使用的代码和数据。****
用简单的例子理解数据丰富
数据丰富在您的数据质量工作中具有特殊的重要性
在主数据管理领域中,有一个低挂的数据质量成果,听起来很简单,但是会将整个 MDM 计划提升到下一个级别。我在这里使用一个“低挂果实”术语,因为这个概念非常简单,但可能需要一些时间来实现,这取决于您的组织需求、专业知识等等。在这个机会中,我不打算深入研究进行数据丰富的技术观点,而是试图解释如何实现数据丰富的概念和想法。
在他们关于多领域多维数据模型的非常实用的书中,马克·艾伦和道尔顿·塞尔沃将数据丰富术语阐述为通过补充缺失或不完整的数据来增强现有信息的过程。众所周知,许多组织,尤其是大型组织,通常都由独立且分散的信息系统组成,每个信息系统都保存着自己的数据。例如,客户数据可以用不同的格式和完整性级别在不同的应用程序中表示和存储。一个系统存储特定客户的地址信息,而另一个系统出于某种原因只存储他/她的 ID,这种情况并不少见。
因此,丰富信息可以通过组合一个数据源和另一个数据源来完成。此外,我们可以通过来自数据本身的信息来丰富数据,如果数据被证明在其中有一些嵌入的智能。现在让我们深入这个简单的例子,以便更好地理解。
假设您有一份需要充实的客户数据记录,因为这将是您的黄金数据。该信息表示如下:
原始数据
初始数据只包含姓名和 ID。看看其他数据源,看看我们是否能找到额外的属性来完善我们的黄金数据,这可能很有诱惑力。但是坚持住,不要看得太远,先检查一下你现有的数据是否有一些嵌入式智能。
一些国家的 ID 号中存储了一些数据,我们可以提取这些数据来获取信息。在这个简单的场景中,如果我们看一下我们的虚拟 ID 号,我们可以看到其中包含如下信息…
虚拟编号,仅用于场景目的
瞧啊。在不涉及外部数据的情况下,我们可以仅从客户的 ID 推断出该客户的 DoB
请注意,并非所有类型的标识符号码都具有嵌入式智能。事实上,为了防止欺诈和数据滥用,产生没有存储信息的随机标识符号码现在是一种日益增长的趋势(不久将成为最佳实践)。现在,您的工作是检查您用来识别客户的身份证或任何类型的识别码,并确定是否可以提取任何信息来丰富您的数据。
随着 DoB 问题的解决,现在我们还剩下两个柱需要充实。既然我们已经从现有数据中提取了所有可用的信息,那么现在是时候从其他数据源中寻找了。
假设现在我们有来自系统 A 另一个数据源,它提供如下信息
来自系统 A 的数据
一切看起来都很好,除了一个争论,这个' M.J .卡特'是否与我们的' Margaret Johnson Carter '相同。
在有时间解决之前,另一个数据源从系统 B 弹出,表示为
来自系统 B 的数据
名字看起来一样,ID 看起来极其一样……除了最后一个数字……
虽然您现在已经有了完成黄金数据表的所有必要信息,但您仍然需要头疼地确定这些“M . J Carter”和“Margaret 女士”是否代表与“Margaret Johnson Carter”相同的实体。我想强调的是,这种令人头疼的问题在任何组织中都很常见,因为信息分散在不同的系统中,每个系统都可能属于不同的部门。也有可能是打字错误和其他人为错误,使事情变得更糟。这就是主数据管理工具派上用场的地方(参见我以前的关于 MDM 的文章)
基本上,您需要对所有这些数据进行“匹配”,以确定它们是否代表同一个实体。在进行匹配时,根据相似性比较所有数据属性,并分配最终分数以确定数据是否相同。分数越高,您就越有把握数据代表的是同一个实体。要做到这一点,您可以使用数据匹配工具或发挥您的力量,做一些手动验证。
为了完成我们的场景,假设在做了一些人工验证后,我们现在有足够的信心说这三个名字是同一个人。而现在…
我们完成了数据丰富工作:)
从上面这个非常简单的场景中,我希望你抓住数据丰富工作的概念,并得到一些关于你可以用你的数据做什么的启发。请记住,您不需要手动完成所有这些工作,因为现代 MDM 工具都配备了数据丰富功能。有些可能足够复杂,可以连接到组织外部的数据源,以检索通常在组织内部无法获得的信息。
永远记住,丰富的数据包含更强大的信息,因此被滥用的风险更大。数据安全应该始终是我们最关心的问题。
下次见!
理解 Python 中的数据结构
最有用的内置数据结构概述。
照片由 Riho Kroll 在 Unsplash 上拍摄
ython 有很大的可能性来处理我们的数据。让我们仔细看看列表、元组/命名元组、集合和字典的功能。我不打算深入研究,例如,如何在列表中添加或删除元素。我的目标是用简单直接的方式展示每个集合的操作,举例说明它们的陈述。在每个解释的最后,将展示每个系列的主要特征。这些例子将用 Python 语言编写,但是由于重点是概念而不是应用程序,所以您可以将这些应用到您的编程语言中,这可能会以非常相似的方式工作。
实验数字电视系统
2020–06–18
- 从 Python 3.7 开始,字典保证保持插入顺序。坦克让-弗朗索瓦·科比特
元组
元组是可以按顺序存储特定数量的其他对象的对象。一旦元素被放入 tuple 中,它们就变得不可变,这意味着您将无法在运行时添加、删除或移动这些元素。反过来,元组在大多数情况下用于存储数据,因此它的主要功能是在一个地方存储不同类型的数据。元组有两种声明方式,不管是否使用括号(,它们的项之间用逗号分隔。
带括号和不带括号的元组示例。
items = ('Name', 75, 80, 90)
items = 'Name', 75, 80, 90
此外,我们可以在元组中添加函数,例如:
items = calculate((“Name”, 75, 80, 90), datetime.date(2020,01,01))
在这个例子中,我们需要括号,所以函数的第一个参数是我们的元组,第二个是数据,最后,我们关闭函数的括号,这样我们的 Python 解释器将知道函数的开始和结束位置。
主要特征:
- 无序的
- 不变的
- 比列表更快
命名元组
上面我们看到了元组的使用,但在某些情况下,我们可能希望单独访问我们的一些键,让我们分析下面的例子,其中我希望使用元组来表示 RGB 中的颜色。
rgb_color = (55, 155, 255)
我们可以同意,对这个作业的解读并不完全错误。读到这里,我可以确切地理解,当访问 rgb_color [0]键时,它反过来将返回值 55。通过对变量的描述,我知道值 55 代表 RGD 红,但在大多数情况下,这可能不是显而易见的,要么是由于使用的上下文,要么是由于开发人员的经验。举例说明了使用命名元组处理这种情况的更好方法。
from collections import namedtuple
Color = namedtuple("Color", "red blue green")
color = Color(red=55, blue=155, green=255)
命名元组的声明类似于传统元组的声明,在我们的 named tuple 中,作为第一个参数,我们必须通知我们的标识符,第二个参数字符串用空格或逗号分隔。结果是一个与传递的标识符完全相同的对象。这样,我们可以通过访问它的一个属性来解包您的值,例如:cor.blue
主要特征:
- 类似于元组
- 让您的元组自文档化。
- 不需要使用索引来访问元素
目录
列表是几个项目序列的概念,其中,根据需要,您需要将它们分组或按顺序放置。列表的一个特性是,如果有必要,我们可以单独访问列表的每个位置,在 Python 中,它是在两个方括号[]之间声明的,它的内部项由逗号(,)分隔。
fruits = [‘apple’, ‘banana’, ‘orange’]
除了已经举例说明的数据之外,您还可以用其他类型的数据填充您的列表,比如对象、整数、元组甚至列表的列表。根据定义,我们应该总是优先在我们的列表中存储相同类型的数据,如果你觉得有必要做这种类型的操作,也许你应该看看字典提供了什么。
主要特征:
- 整齐的
- 易变的
- 接受重复元素
- 按索引访问元素
- 可以嵌套
- 动态增长
设置
您可以将该集合视为列表的扩展,在您想要存储唯一值的情况下应该使用它,而列表本身并没有提供这些值。它的主要特性是存储唯一的和无序的值。在 Python 中,在某些时候,你必须意识到所有的东西都是对象,默认情况下,我们的集合只存储每个对象的相同副本。集合在初始声明时用括号()声明。
在下面的示例中,我们有一个列表,其中包含汽车型号和品牌的元组:
brand_model = [("Fiesta", " Ford"), ("Gol", "VW"), ("KA", "Ford"), ("Uno", Fiat)]
unique_brands = set()
for model, brand in brand_model:
unique_brands.add(brand)
>>> {'Fiat', 'Ford', 'VW'}
这个结果显示了我们独特的汽车品牌,一个重要的细节是,我们的 set 并不按照插入的顺序将结果返回给我们,就像字典一样,Set 并不保证其元素的顺序。我们可以利用集合的一个很好的用途是能够看到集合中有什么或没有什么。
主要特征:
- 无序的
- 不允许重复项目
- 与列表相比,访问速度更快
- 支持数学运算(并、交、差)
字典
根据我作为开发人员的经验,字典是使用 Python 操作数据的最简单和最有效的方式,因为您的问题和数据都可以使用字典。基本上,它在键和值存储中是有意识的,所以 redis 的主要目标是存储来自键-值对的信息,例如:color = blue,其中 color 是我的键,blue 是我的值。非常类似哈希内容,其他语言的 hashmap。在 Python 中,它是在两个大括号{}之间声明的,其内部项用逗号(,)分隔。
car = {'model': 'Ka', 'brand': 'Ford', 'Year': 2012}
主要特征:
- 无序的
- 存储不同类型数据的能力
- 可以嵌套
- 动态增长
结论
展示的每个系列都有自己的特色,每个系列都必须用于特定的场合。它们的结合使用为我们处理数据提供了极大的灵活性,并使我们解决实际问题的主要目标变得更加容易。
进一步阅读。
如果你想更深入地理解或者看到不同的方法,我在 Medium 上挑选了一些深入研究这个主题的文章。好好学习。
Python 集合模块的快速概述。
towardsdatascience.com](/pythons-collections-module-high-performance-container-data-types-cb4187afb5fc) [## Python 基础-数据结构
Python 内置集合简介
blog.usejournal.com](https://blog.usejournal.com/python-basics-data-structures-d378d854df1b) [## 您应该始终使用的 Python 集合
Python collections 是一个被低估的库,它可以将您的编码提升到下一个级别。
medium.com](https://medium.com/swlh/python-collections-you-should-always-be-using-b579b9e59e4)
从头开始理解 DBSCAN 算法和实现
DBSCAN 算法分步,Python 实现,可视化。
什么是 DBSCAN
DBSCAN(带噪声的基于密度的应用空间聚类)是一种常用的无监督聚类算法,于 1996 年提出。与众所周知的 K-mean 不同,DBSCAN 不需要指定聚类数。它可以根据您输入的数据和参数自动检测集群的数量。更重要的是,DBSCAN 可以发现 k-means 不能发现的任意形状的聚类。例如,被不同群集包围的群集。
DBSCAN vs K-means,信用
此外,DBSCAN 可以处理噪声和异常值。所有离群值将被识别和标记,而不被分类到任何聚类中。因此,DBSCAN 也可以用于异常检测(异常值检测)
在我们查看 preusdecode 之前,我们需要先了解一些基本概念和术语。Eps、Minpits、直接密度可达、密度可达、密度连通、核心点和边界点
首先,我们需要为 DBSCAN、Eps 和 MinPts 设置两个参数。
Eps:小区最大半径
MinPts:该点的 Eps 邻域中的最小点数
并且有直接密度可达的概念:一个点 p 是从一个点 q 直接密度可达 w.r.t. Eps,MinPts,如果 NEps (q): {p 属于 D | dist(p,q) ≤ Eps}并且|N Eps (q)| ≥ MinPts。我们来看一个 Minpts = 5,Eps = 1 的例子。让我们看一个例子来理解密度可达和密度连通。
密度可达示例
密度相关示例
最后,如果一个点在 Eps 中有超过指定数量的点(MinPts ),那么这个点就是核心点。这些是在聚类 a 内部的点。并且边界点在 Eps 内具有比 MinPts 少的点,但是在核心点的邻域内。我们还可以定义离群点(噪声点),即既不是核心点也不是边界点的点。
核心点、边界点、异常点示例
现在,我们来看看 DBSCAN 算法实际上是如何工作的。这是前解码。
- 任意选择一个点 p
- 基于 Eps 和 MinPts 检索从 p 密度可达的所有点
- 如果 p 是一个核心点,就形成了一个集群
- 如果 p 是一个边界点,则没有从 p 密度可达的点,DBSCAN 访问数据库的下一个点
- 继续该过程,直到处理完所有点
如果使用空间索引,DBSCAN 的计算复杂度为 O(nlogn),其中 n 是数据库对象的数量。否则,复杂度为 O(n )
例子
考虑以下 9 个二维数据点:
x1(0,0),x2(1,0),x3(1,1),x4(2,2),x5(3,1),x6(3,0),x7(0,1),x8(3,2),x9(6,3)
使用欧几里德距离,Eps =1,MinPts = 3。找到所有的核心点、边界点和噪声点,并使用 DBCSAN 算法显示最终的聚类。让我们一步步展示结果。
数据可视化示例
首先,计算 N(p),Eps-点 p 的邻域
N(x1) = {x1,x2,x7}
N(x2) = {x2,x1,x3}
N(x3) = {x3,x2,x7}
N(x4) = {x4,x8}
N(x5) = {x5,x6,x8}
N(x6) = {x6,x5}
N(x7) = {x7,x1,x3}
N(x8) = {x8,x4,x5}
N(x9) = {x9}
如果 N(p)的大小至少为 MinPts,则称 p 为核心点。这里给定的 MinPts 是 3,因此 N(p)的大小至少是 3。因此核心点是:{x1,x2,x3,x5,x7,x8}
然后根据边界点的定义:给定一个点 p,若 p 不是核心点但 N(p)包含至少一个核心点,则称 p 为边界点。N(x4) = {x4,x8},N(x6) = {x6,x5}。这里 x8 和 x5 是核心点,所以 x4 和 x6 都是边界点。显然,左边的点, x9 是一个噪声点。
现在,让我们按照 preusdecode 生成集群。
- 任意选择一个点 p,现在我们选择 x1
- 检索从 x1: {x2,x3,x7}密度可达的所有点
- 这里 x1 是一个核心点,形成一个集群。因此,我们有 Cluster_1:
- 接下来,我们选择 x5,检索从 x5 密度可达的所有点:
- 这里 x5 是一个核心点,形成一个集群。因此,我们有了 Cluster_2:
- 接下来,我们选择 x9,x9 是一个噪声点,噪声点不属于任何聚类。
- 因此,算法到此为止。
最终 DBSCAN 集群结果
Python 实现
下面是一些示例代码,用于从头开始构建 FP-tree,并在 Python 3 中找到所有的频率项集。我还添加了点的可视化,并用蓝色标记所有异常值。
感谢您的阅读,我期待听到您的问题和想法。如果你想了解更多关于数据科学和云计算的知识,可以在Linkedin上找我。
照片由 Alfons Morales 在 Unsplash 拍摄
参考
https://github.com/NSHipster/DBSCAN
https://en.wikipedia.org/wiki/DBSCAN
理解决策树分类器
概念上以使用 ID3 算法为例
凯文·Ku 在 Unsplash 上的照片
在这篇文章中,我们将从概念上讨论决策树分类器的工作原理,这样它以后就可以应用到现实世界的数据集中。
分类可以定义为学习一个目标函数 f 的任务,该目标函数将每个属性集合 x 映射到一个预定义的标签 y 。
例子:
- 将一条新闻分配给一个预定义的类别。
- 根据邮件标题和内容检测垃圾邮件
- 基于 MRI 扫描的结果将细胞分类为恶性或良性
- 根据形状对星系进行分类
作为数据科学家或机器学习工程师,在处理真实世界数据集时,决策树可能是一个强大的工具。当您构建随机森林分类器时,也可以串联使用决策树,该分类器是多个决策树共同工作的顶点,根据多数投票对记录进行分类。
一个决策树是通过对我们得到的数据集的一个记录提出一系列问题来构建的。每次收到一个答案,都会询问后续问题,直到对记录的类别标签得出结论。这一系列问题及其可能的答案可以以决策树的形式组织起来,这是一种由节点和有向边组成的层次结构。树有三种类型的节点:
- 根节点,没有输入边,有零个或多个输出边。
- 内部节点,每个节点都有一条输入边和两条或多条输出边。
- 叶节点或终端节点,每个节点只有一条输入边,没有输出边。
在决策树中,每个叶节点被分配一个类标签。包括根节点和其他内部节点在内的非终端节点包含属性测试条件,用于分隔具有不同特征的记录。
在深入研究数据集的数学之前,让我们根据给定的数据集直观地构建一个决策树。
作者的样本数据集|图像
当我们检查这个数据集时,我们看到:
- 属性房屋所有者本质上是二元的,仅由是 & 否组成
- 属性婚姻状况本质上是分类的,包括单身、已婚&离婚
- 属性年收入本质上是连续的,由数值组成
- 属性默认借款人本质上是二元的,仅由是 & 否组成,是我们的目标属性或类标签
给定这个数据集,为了直观地构建决策树,我们从目标属性默认借款人开始
- 初始树包含一个节点,其类别标签为默认借款人=否
- 树需要细化,因为这个根节点包含来自两个类的记录
- 记录随后根据房屋所有者条件的结果进行划分
- 这是递归完成的
- 从上面的数据集中,所有拥有房屋的借款人(房屋所有人=是)都成功偿还了贷款。然后,根节点的左子节点因此被标记为默认借款人=否
- 对于正确的孩子,我们需要应用递归步骤,直到所有记录都属于同一个类
上述步骤可能在一次阅读中就能消化,我鼓励你再读一遍,看看下面的图,以了解这些步骤是如何进行的。
使用直觉构建决策树|作者图片
决策树归纳的设计问题
上面的树归纳附带了几个在构建决策树时需要注意的问题。
→培训记录应该如何拆分?
对于树生长过程的每个递归步骤,必须选择一个属性测试条件,以进一步将记录划分为更小的子集。
→分割程序应该如何停止?
继续展开节点,直到所有记录都属于同一个类,或者所有记录都具有相同的属性值。
表示属性测试条件的方法
决策树归纳算法必须提供一种方法来表达属性测试条件及其对不同属性类型的相应结果。
- 二元属性:两种可能的结果。
二元属性分割|按作者分类的图像
- 名义属性: a)多路拆分
b)二进制拆分
多向属性拆分(左)和二进制属性拆分(右)|作者图片
- 序数属性: a)多路拆分
b)二进制拆分
序数属性按作者拆分|图像
注意:只要分组不违反属性值的顺序属性,属性值就可以分组在一起。
- 连续属性:测试条件可以表示为具有二元结果的比较测试(A < v)或(A≥v ),或具有 v(i) ≤ A ≤ v(i+1)形式的结果的范围,其中 i = 1,2,3… k
a)对于二元情况,算法必须考虑所有可能的分割位置 v,并选择产生最佳分割的位置。
b)对于多路分割,算法必须考虑所有可能的连续值范围。
连续属性分割|按作者分类的图像
选择最佳分割的方法
为选择最佳分裂而开发的度量通常基于子节点的不纯程度。杂质越小,阶级分布越不均匀。
示例:具有类分布(0,1)的节点具有零杂质,而具有均匀类分布(0.5,0.5)的节点具有最高杂质。
杂质测量
杂质测量|作者图片
在哪里,
D =训练集,其中 v 属于 D 并且是树节点,
L = {y1,y2,…,yk}标签/类别集
让我们用一些例子来理解上面的杂质测量:
行动中的杂质测量|作者图片
杂质测量比较|作者图片
→为了确定一个测试条件执行得有多好,我们需要比较父节点(分裂前)和子节点(分裂后)的不纯程度。差异越大,测试条件越好。增益是一个可用于确定分割质量的标准。
对于将属于 A 的属性 A 的值分割成子节点(v,A)的节点 v,分割的增益为:
信息增益|作者图片
注:Inf 增益是信息增益
增益比
熵和基尼指数等杂质度量往往倾向于具有大量不同值的属性。有两种策略可以克服这个问题。第一个策略是将测试条件限制为仅二进制分割。这种策略被诸如 CART 之类的决策树算法所采用。另一个策略是修改分割标准,以考虑由属性测试条件产生的结果的数量。例如,在 C4.5 决策树算法中,称为增益比的分割标准用于确定分割的好坏。该标准定义如下:
增益比|作者图片
在哪里,
增益比的组成部分|作者图片
作者对上图中求和条件的解释
ID3 算法
ID3 算法伪代码|图片作者
让我们用一个样本数据集来理解这个算法。我们将使用的衡量标准是信息增益来构建我们的决策树。
样本数据集|源 Tan 的数据挖掘
我没有输入长的方程式,而是用图片来解释我使用的所有快捷键。
目标属性的熵|按作者分类的图像
在上图中 Y =是,N =否
游戏信息增益,Windy |作者图片
在上面的图片中,W = T 和 W = F 是真和假,属性 Windy 的值
游戏、展望|作者图片的信息增益
游戏、展望|作者图片的信息增益
在上图中,O = S,O = O,O = R 是属性 Outlook 的晴天、阴天和雨天的值
游戏信息增益,温度|作者图片
在上图中,温度。= Temperature 和 T=H,T=M,T=C 是属性 Temperature 的高温、低温和低温值
玩耍的信息增益,湿度|作者图片
在上图中,H=H,H=N 是湿度属性的高值和正常值
因为 outlook 的收益最高,所以 Outlook 是我们的根节点。
现在,用 Outlook = Sunny 计算第一个子节点
展望的信息增益=晴朗,温度|作者图片
展望的信息增益=晴天,湿度|作者图片
Outlook 的信息增益=晴朗、多风|作者图片
既然,Outlook 的信息增益=晴天,湿度最高,它就成为我们的第一个子节点。
对于根节点为 Outlook 的第二个子节点,我们考虑配对 Outlook =阴,并发现当 Outlook =阴时,Play = Yes,则直接导致 Yes 的叶节点
对于 Outlook 位于根节点的第三个子节点,我们考虑配对 Outlook = Rainy
展望的信息增益=雨天、气温|作者图片
展望的信息增益=雨天、气温|作者图片
Outlook 的信息增益=雨天、湿度|作者图片
Outlook 的信息增益=下雨、刮风|作者图片
因为 Outlook = Rainy,Windy 的信息增益最高,所以它成为我们的第三个子节点。
继续向下,深入到第一个子节点,其中 Outlook = Sunny 和湿度,我们观察到湿度的分割是纯的,因此我们不需要计算信息增益并直接放置叶节点。
同样的情况发生在 Outlook = Rainy 和 Windy 上,我们观察到 Windy 上的分裂是纯的。
意思是
- 当前景=晴朗且湿度=高时,目标属性 Play = No
- 当前景=晴朗,湿度=正常时,目标属性 Play = Yes
- 当 Outlook = Rainy and Windy = True 时,目标属性 Play = No
- 当 Outlook = Rainy and Windy = False 时,目标属性 Play = Yes
最终的决策树如下所示:
最终决策树|作者图片
我将免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
感谢您的阅读。这篇文章到此结束。我希望我能够使决策树的主题更容易理解。这只是决策树领域中使用的一个非常基本的算法。还有更多的算法,我建议读者在理解 ID3 算法后看一看,因为这是所有算法的基础,也是理解其余算法所需要的。
此外,我已经写了一篇的帖子,使用 Python3 和 XML 从零开始实现上述决策树,而没有使用任何机器学习库。
使用 Python 和 XML 的 ID3 算法
medium.com](https://medium.com/swlh/machine-learning-decision-tree-implementation-849df3ce36d2)
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
这是我的故事索引:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
理解卷积神经网络中的深度关联嵌入
一种无需标记即可对预测进行分组的优雅方法
Alex Alvarez 在 Unsplash 上的照片
在计算机视觉和深度学习的一些任务中,我们需要先预测所有的结果,然后将结果拆分成几个单独的结果。
在这种精神下,一个常见的任务是多人的姿势估计,其中首先预测图像中所有人的关键点,然后将其分割成单个姿势作为最终预测(图 1)。
图 1:多人的姿势估计
本着这种精神的另一个任务是通过检测和分组对象的成对关键点来形成边界框的对象检测,与 R-CNN 系列作品[Ren 等人]相比,这是形成边界框的相对较新的方法。在他们的新方法[Law 等人]中,每个对象的边界框由两个关键点表示(左上角和右下角)。在推断过程中,首先预测图像中所有对象的关键点,然后分裂成单独的关键点对以获得最终预测(图 2)。
图 2:用于物体检测的角网[Law 等人]
以上两项工作都需要将所有预测的关键点拆分到单独的组中。为了实现在神经网络中作为副作用,分裂和分组算法应该同时满足两个方面:1)属于同一对象的关键点应该被正确地分配到同一组;2)哪一个关键点属于哪一个群体应该是内在理解的,不需要额外的标注工作。
联想嵌入[Newell et al.]正是实现上述讨论的一个很好的选择。
联想嵌入
让我们以多人姿势估计为例。为了将所有预测的关键点分组到每个个体,标签也与每个关键点一起被预测,并且预测的标签应该满足两个方面:1)同一个人的标签应该尽可能相等;2)不同人的标签应该容易区分。根据这些方面,Newell 等人提出了以下损失函数来训练标签值(等式。1).
Equ。1:用于学习关联嵌入的标签值的损失函数
在损失函数中,为 N 个人中的每一个预测 K 个关键点和 K 个标签值,从而总共预测 NK 个关键点和 NK 个标签值。
第一项(红色)通过惩罚预测标签值与其平均值的偏差,使同一个人的标签值相似。第二项(蓝色)通过惩罚一个高斯函数使不同人的标签值不同,该高斯函数的峰值在两个不同人的平均标签值相遇的位置,随着两个平均值彼此偏离,损失急剧下降(图 3)。
图 3:等式的第二项的解释。一
有了这个损失函数,每个人的标签值可以自动学习,无需额外标记。因此,联想嵌入快速、轻便,易于嵌入到现有的深度学习架构中。
预测标签值的结果
图 4:用于多人姿势估计的关联嵌入
如图 4 所示,右边部分是预测的标签值,左边部分是相应的姿态估计。右边部分,y 轴是人体关键点的索引,x 轴是标签的值。红色圆圈代表检测到的关键点的标签值。由于部分人的身体部位被遮挡,检测到的关键点数量因人而异。我们可以很容易地看到,不同人的标签值聚集在不同的垂直线中,这很容易单独分组。
因此,关联嵌入可以以精确和轻量的方式将预测结果分组到个体级别。此外,通过简单地添加损失函数,作为机器学习中的常见实践,关联嵌入不仅可以用于计算机视觉任务,还可以作为更广泛的机器学习领域中的其他任务的强大成分。
参考文献
更快的 R-CNN:使用区域建议网络实现实时对象检测,任等,NIPS 2015
关联嵌入:用于联合检测和分组的端到端学习,Newell 等人,NIPS 2017
CornerNet:将对象检测为成对的关键点,Law 等人,ECCV 2018 年
阅读陈数·杜(以及媒体上成千上万的其他作家)的每一个故事。您的会员费直接支持…
dushuchen.medium.com](https://dushuchen.medium.com/membership)
理解具有集成梯度的深度学习模型
理解并实现各种深度学习网络的集成梯度技术,以解释模型的预测
本帖将帮助你理解积分梯度的两个基本公理,以及如何使用迁移学习模型使用 TensorFlow 实现积分梯度。
什么是积分渐变?
集成梯度(IG)是用于深度神经网络的可解释性或可解释性技术,其可视化了有助于模型预测的输入特征重要性
IG 可以只应用于深度学习的特定用例,还是只应用于特定的神经网络架构?
集成梯度(IG)计算模型预测输出到其输入特征和的梯度,不需要对原始深度神经网络进行修改。
IG 可以应用于任何可区分的模型,如图像、文本或结构化数据。
IG 可用于
- 通过从网络中提取规则来理解特征重要性
- 调试深度学习模型性能
- 通过理解对预测有贡献的重要特征来识别数据偏差
积分渐变是如何工作的?
解释 IG 使用深度学习模型进行图像分类
积分梯度建立在两个需要满足的公理之上:
- 灵敏度和
- 实现不变性
灵敏度:
为了计算灵敏度,我们建立一个基线图像作为起点。然后,我们构建一系列图像,从基线图像插值到实际图像,以计算综合梯度。
实现不变性
当两个功能等效的网络对于相同的输入图像和基线图像具有相同的属性时,实现不变性被满足。
当两个网络的输出对所有输入都相等时,尽管它们的实现非常不同,但它们在功能上是等效的。
计算和可视化集成梯度(IG)
步骤 1: 从基线开始,基线可以是像素值全为零的黑色图像或全白图像,也可以是随机图像。基线输入是一种中性的预测,是任何解释方法和可视化像素要素重要性的核心。
第二步:生成基线和原始图像之间的线性插值。插值图像是基线和输入图像之间的特征空间中的小步长(α),并且随着每个插值图像的强度不断增加。
步骤 3:计算梯度以测量特征变化和模型预测变化之间的关系。
梯度告知哪个像素对模型预测的类别概率具有最强的影响。
改变变量会改变输出,并且变量将获得一些属性来帮助计算输入图像的特征重要性。不影响输出的变量没有属性。
第四步:通过平均梯度计算数值近似值
步骤 5:将 IG 缩放到输入图像以确保在多个插值图像上累积的属性值都是相同的单位。用像素重要性表示输入图像上的 IG。
如何使用 Tensorflow 实现积分渐变?
导入所需的库
**import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input as mobilenet_v2_preprocess_input**
使用 MobileNetV2 作为 Imagenet 数据集上的传输学习模型
**model = tf.keras.applications.MobileNetV2(input_shape=(224,224,3), include_top=True, weights='imagenet')**
加载 Imagenet 标签
**def load_imagenet_labels(file_path):
labels_file = tf.keras.utils.get_file('ImageNetLabels.txt', file_path)
with open(labels_file) as reader:
f = reader.read()
labels = f.splitlines()
return np.array(labels)
imagenet_labels = load_imagenet_labels('**[**https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt'**](https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')**)**
加载并预处理图像
**def read_image(file_name):
image = tf.io.read_file(file_name)
image = tf.image.decode_jpeg(image, channels=3)
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.image.resize_with_pad(image, target_height=224, target_width=224)
return image****img = {'Peacock':'Peacock.jpg'}****img_name_tensors = {name: read_image(img_path) for (name, img_path) in img.items()}**
显示原始输入图像
**plt.figure(figsize=(5, 5))
ax = plt.subplot(1, 1, 1)
ax.imshow(img_name_tensors['Peacock'])
ax.set_title("Image")
ax.axis('off')
plt.tight_layout()**
预测输入图像模型的前三个预测值
**def top_k_predictions(img, k=3):
image = tf.expand_dims(img, 0)
predictions = model(image)
probs = tf.nn.softmax(predictions, axis=-1)
top_probs, top_idxs = tf.math.top_k(input=probs, k=k)
top_labels = np.array(tuple(top_idxs[0]) )
return top_labels, top_probs[0]**#Display the image with top 3 prediction from the model
**plt.imshow(img_name_tensors['Peacock'])
plt.title(name, fontweight='bold')
plt.axis('off')
plt.show()****pred_label, pred_prob = top_k_predictions(img_name_tensors['Peacock'])
for label, prob in zip(pred_label, pred_prob):
print(f'{imagenet_labels[label+1]}: {prob:0.1%}')**
创建一个黑色基线图像,作为计算特征重要性的起点
**baseline = tf.zeros(shape=(224,224,3))**
生成基线和原始输入图像之间的线性插值
图像插值
**m_steps=50
alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)** **def interpolate_images(baseline,
image,
alphas):
alphas_x = alphas[:, tf.newaxis, tf.newaxis, tf.newaxis]
baseline_x = tf.expand_dims(baseline, axis=0)
input_x = tf.expand_dims(image, axis=0)
delta = input_x - baseline_x
images = baseline_x + alphas_x * delta
return images****interpolated_images = interpolate_images(
baseline=baseline,
image=img_name_tensors['Peacock'],
alphas=alphas)**
可视化插值图像
**fig = plt.figure(figsize=(20, 20))****i = 0
for alpha, image in zip(alphas[0::10], interpolated_images[0::10]):
i += 1
plt.subplot(1, len(alphas[0::10]), i)
plt.title(f'alpha: {alpha:.1f}')
plt.imshow(image)
plt.axis('off')****plt.tight_layout();**
插值图像
计算模型输出和插值输入之间的梯度
计算梯度测量特征变化和模型预测变化之间的关系。我们用 tf。GradientTape 计算插值图像与顶部预测类 Id 之间的梯度,顶部预测类 Id 指示哪些像素对模型预测的影响最大
**def compute_gradients(images, target_class_idx):
with tf.GradientTape() as tape:
tape.watch(images)
logits = model(images)
probs = tf.nn.softmax(logits, axis=-1)[:, target_class_idx]
return tape.gradient(probs, images)****path_gradients = compute_gradients(
images=interpolated_images,
target_class_idx=84)**
使用黎曼梯形累积梯度
**def integral_approximation(gradients):**
# riemann_trapezoidal
**grads = (gradients[:-1] + gradients[1:]) / tf.constant(2.0)
integrated_gradients = tf.math.reduce_mean(grads, axis=0)
return integrated_gradients**
将所有步骤放入一个函数中以计算积分梯度
[**@tf**](http://twitter.com/tf)**.function
def integrated_gradients(baseline,
image,
target_class_idx,
m_steps=50,
batch_size=1):**
# 1\. Generate alphas.
** alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)**# Initialize TensorArray outside loop to collect gradients.
** gradient_batches = tf.TensorArray(tf.float32, size=m_steps+1)**
# Iterate alphas range and batch computation for speed, memory #efficiency, and scaling to larger m_steps.
**for alpha in tf.range(0, len(alphas), batch_size):
from_ = alpha
to = tf.minimum(from_ + batch_size, len(alphas))
alpha_batch = alphas[from_:to]**# 2\. Generate interpolated inputs between baseline and input.
** interpolated_path_input_batch = interpolate_images(baseline=baseline, image=image, alphas=alpha_batch)**# 3\. Compute gradients between model outputs and interpolated inputs.
**gradient_batch = compute_gradients(images=interpolated_path_input_batch, target_class_idx=target_class_idx)**
# Write batch indices and gradients to extend TensorArray.
**gradient_batches = gradient_batches.scatter(tf.range(from_, to), gradient_batch) **
# Stack path gradients together row-wise into single tensor.
** total_gradients = gradient_batches.stack()**# 4\. Integral approximation through averaging gradients.
**avg_gradients = integral_approximation(gradients=total_gradients)**# 5\. Scale integrated gradients with respect to input.
**integrated_gradients = (image - baseline) * avg_gradients****return integrated_gradients****ig_attributions = integrated_gradients(baseline=baseline, image=img_name_tensors['Peacock'], target_class_idx=84, m_steps=283)**
可视化属性和综合梯度以解释对输入图像的预测
**def plot_img_IG(baseline,
image,
target_class_idx,
m_steps=50,
cmap=None,
overlay_alpha=0.4):** **attributions = integrated_gradients(baseline=baseline, image=image, target_class_idx=target_class_idx, m_steps=m_steps)** **attribution_mask = tf.reduce_sum(tf.math.abs(attributions), axis=-1)** **fig, axs = plt.subplots(nrows=1, ncols=2, squeeze=False, figsize= (8, 8))
axs[0, 0].set_title('Attribution mask')
axs[0, 0].imshow(attribution_mask, cmap=cmap)
axs[0, 0].axis('off')** **axs[0, 1].set_title('Overlay IG on Input image ')
axs[0, 1].imshow(attribution_mask, cmap=cmap)
axs[0, 1].imshow(image, alpha=overlay_alpha)
axs[0, 1].axis('off')** **plt.tight_layout()
return fig****_ = plot_img_IG(image=img_name_tensors['Peacock'],
baseline=baseline,
target_class_idx=84,
m_steps=240,
cmap=plt.cm.inferno,
overlay_alpha=0.4)**
结论:
集成梯度(IG)通过突出特征重要性来帮助你解释深度学习模型看什么来进行预测。这是通过计算模型的预测输出到其输入要素的梯度来实现的。它不需要对原始的深度神经网络进行任何修改,可以应用于图像、文本以及结构化数据。IG 基于敏感性和实现不变性两个公理。
参考资料:
本教程演示了如何实现综合梯度(IG),一个可解释的人工智能技术介绍了…
www.tensorflow.org](https://www.tensorflow.org/tutorials/interpretability/integrated_gradients)
http://theory y . Stanford . edu/~ ataly/Talks/Sri _ attribution _ talk _ jun _ 2017 . pdf
了解检测器 2 演示
了解脸书人工智能研究图书馆,了解最先进的神经网络
使用检测器 2 ( 源)进行实例分割
简介
Detectron2 ( 官方库 Github )是“FAIR 的下一代物体检测和分割平台”。FAIR(脸书人工智能研究所)创建了这个框架,以提供 CUDA 和 PyTorch 实现最先进的神经网络架构。它们还为对象检测、实例分割、人物关键点检测和其他用途提供预训练模型。
Detectron2 的重要但经常被忽略的特性是它的许可方案:库本身是在 Apache 2.0 许可下发布的,预训练模型是在 CC BY-SA 3.0 许可下发布的。这意味着你可以修改现有的代码,将其用于私人、科学甚至商业目的。你所需要做的就是给 FAIR 提供适当的信用。这在科学界很少见,科学界经常使用许可证来强制代码源发布和非商业使用。这非常有限,但幸运的是,对于 Detectron2 来说,情况并非如此。
然而,问题是,研究人员编写的代码通常不遵循干净的代码指南。对于 Detectron2 来说,与替代方案相比,它还不错,但是代码结构肯定很复杂,需要花很大力气才能理解。然而,要使用这个库的强大功能,确实需要了解它。在这篇文章中(希望还有后面的文章),我的目标是阐明 API、代码结构以及如何修改它并在你的项目中使用它。
下面的代码假设你已经安装好了所有的东西,并且正在运行。特别是,你需要一个 Linux 系统(Windows 可能工作,但不被官方支持),支持 CUDA 的 GPU(安装了 CUDA),PyTorch >= 1.4 和适当的 Detectron2 版本。鉴于运行这些神经网络所需的计算能力,该库目前不支持 CPU 计算,并且可能会继续支持。如果你们中的许多人在这方面遇到了问题,请在评论中告诉我,我会写另一篇关于所有适当工具的设置的文章。
基本设置
我们将通过修改的 Google Colab 演示使用 COCO 数据集类进行实例分割。目标是创建易于理解的代码框架,完美地用于未来基于 Detectron2 的项目。让我们从获取命令行参数开始:
作为技术细节,我们将编写 Python 3.5 中引入的 Python 类型注释。它们并不强制变量类型,而是旨在帮助程序员(以及 ide,它们提供了更好的注释帮助)更好地理解代码。这样也更容易知道在 Detectron2 文档和源代码中的何处寻找关于模型行为的线索。
这里发生了一些重要的事情。首先,我们导入了argparse
模块,以便于参数解析。我们的演示可能需要 2 个参数:在 Detectron2 中使用的基本模型和一个或多个要处理的图像的列表。为解析和获取参数定义单独的函数是在demo.py
(官方 Detectron2 演示文件)和其他建立在它之上的项目(如 Centermask2)中是如何完成的。总的来说,这也是一个很好的代码实践。名称空间类型的行为类似于 Python 字典,但是对值的访问类似于对类中属性的访问,例如args.base_model
。如果没有提供参数,也没有设置 default,那么它将是适合该参数的空类型,通常是 None(或者是空列表,用于任意数量的选项,如上面的images
)。
重要的部分是基础模型的默认值。它将用于告诉检测器 2 应该使用模型动物园中的哪个预训练模型(基线)。模型动物园是一组由 FAIR 预先训练的模型,可以通过库 API 轻松下载。整个名单可以在这里找到。表格包含关于不同模型的各种统计数据,这些统计数据按照精度度量进行升序排序(比如用于实例分段的方框 AP)。根据你的需要,速度或准确性可能更重要;还要注意,这是平均 AP,在特殊情况下,平均分数较低的模型(最有可能是不太敏感的模型)可能工作得更好。我根据我的个人经验选择了默认值——X101-FPN 往往会给出太多的误报,而且在实践中它比 R101-FPN 慢 1.5-2 倍。为了获得标识您感兴趣的任何型号的字符串,请按照下面显示的步骤操作。
选择您想要使用的模型
突出显示的部分是模型路径—使用它来告诉 Detectron2 您想要使用动物园中的哪个模型
第一次使用模型时,它是从模型动物园下载的,所以可能需要一点时间。
车型配置
现在我们有了基本的设置,是时候配置我们的模型了。Detectron2 实际上需要被告知使用哪个模型,在哪里找到文件等等。幸运的是,使用来自模型动物园的预先训练好的模型非常简单:
首先,我们添加了一些新的导入。它们应该被添加到文件的顶部,紧挨着前面的argparse
导入。get_cfg()
函数只初始化一个空的配置,稍后我们会用所需的设置填充它。类型CfgNode
很少出现在类似这里的几个配置行之外,它的行为类似于argparse.Namespace
,因为属性是通过点来访问的。
merge_from_file()
方法需要一个字符串(本地文件的文件路径)或来自model_zoo.get_config_file()
的配置文件作为参数。它将另一个配置文件加载到我们在cfg
中的配置中。当模型在自定义数据集上接受训练并保存在磁盘上时,通常会使用本地文件。更多情况下,我们只想使用来自 Detectron2 model zoo 的预训练模型,并选择和加载此处由args.base_model
指定的配置文件。
cfg.MODEL.WEIGHTS
是训练时学习的神经网络权重。我们也可以像加载配置文件一样加载它们,只需向模型动物园提供字符串。请注意,实际上我们正在加载一个检查点文件——实际上神经网络训练永远不会“完成”,模型可能会在以后被额外训练。较早停止训练的原因主要是避免过度拟合或缺乏计算能力/时间。当权重以这种方式加载时,使用权重文件中的最后一个检查点(训练最多的神经网络)。
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST
是一个非常重要的可调超参数。它改变了我们的模型对于要被检测的对象必须具有的最小置信度。换句话说,当物体离得很远,不太明显时,等等。该阈值必须更低才能正确检测到它。然而,将其设置得太低可能会导致错误检测或多次检测到相同的对象(这可以通过非最大抑制(NMS)算法来减少,但不能完全消除)。因此,这个超参数需要根据您的具体情况进行调整。50% (0.5)是一个很好的默认值。
现在我们终于准备好初始化将完成所有实际工作的类了— DefaultPredictor
。它只是使用配置中提供的神经网络来进行预测,一次在一台机器和一幅图像上进行预测。一个重要的警告是,它采用 BGR 格式的图像,而不是 RGB 格式——如果您使用 OpenCV ( cv2
)加载图像,这是默认的行为,但其他库如 Pillow 可能使用 RGB 并需要改变格式。
我们终于准备好使用我们的模型了!
执行实例分割
像以前一样,将导入放在文件的开头。新代码遍历提供的图片,并对每张图片进行预测,将它们可视化,并将结果保存到文件中。
使用cv2.imread
自动使用 BGR 格式,所以在这里派上用场。结果图像是一个 3D Numpy 数组,其中维度为:
- 行数(图像高度)
- 列数(图像宽度)
- 表示给定像素的 BGR 值的 3 元素数组(0-255 范围内的整数)
正如你所看到的,下面几行我们使用了[:, :, ::-1]
切片,这意味着“取所有行、所有列和所有第三维,但是颠倒最后一个的顺序”。这意味着为了可视化,我们将使用 RGB——没有它,颜色会很奇怪(但是你当然可以自己尝试!).
加载部分之后是真正的事情:使用predictor(img)["instances"]
来实际使用神经网络并进行实例分割。预测器返回一个只包含一个键-值对的字典,即映射到实例对象的“实例”键。这是一个专门为在 Detectron2 中返回结果而设计的类。它充当关于图像和实际结果的元数据的存储。实例对象包含相当多的信息,足够写一篇单独的文章,但是现在我们可以在不弄乱其内部结构的情况下使用它。现在重要的是要记住 predictor 在 GPU 上工作,并返回仍然在 GPU 上的数据,包括许多 PyTorch 张量。如果您想在 CPU 上处理这些数据(例如,大多数库只处理 PyTorch CPU tensors 或 Numpy 数组),您必须用.to("cpu")
函数显式地转换它,我们在下面几行中做了这些。
Visualizer
是一个用于在图像上绘制来自 Detectron2 神经网络(不仅仅是实例分割,还有其他类型)的结果的类(对于视频你应该使用VideoVisualizer
)。它的论据是:
img_rgb
:进行预测的基础图像metadata
:提供来自数据集的附加数据,比如类(类别)名称映射。在 Detectron 内部,处理的是类编号,而不是名称——它们首先使用元数据从字符串转换成数字,在这里,我们给 Visualizer 这些信息以将数字映射回字符串。这样,我们将显示“汽车”而不是“2”。这里我们只传递训练期间使用的元数据(来自该模型的 COCO 数据集)scale
:改变输出图像的尺寸,如果您的输入图像太小或太大,这很有用
调用draw_instance_predictions
告诉 Visualizer 我们已经完成了实例分割,并将结果作为参数传递。我们首先需要将它发送到 CPU,因为可视化代码不像神经网络那样在 GPU 上运行。它返回VisImage
对象,这是一个带有一些附加信息(比例、宽度和高度)的图像包装器。get_image()
方法从中提取图像矩阵。我们也应用与上面相同的技巧将其更改为 RGB。
剩下的就是保存文件了。事实证明,在文件名后附加“_processed”将文件保存在原文件旁边并不容易,regex 是最简单的方法。"(.*)\."
提取完整的文件路径和文件名,直到扩展名之前的点,并用.group(0)
捕获。用[:-1]
去掉圆点,然后我们可以更改名称(和扩展名,因为 OpenCV 知道如何处理”。png”在目标文件路径中)。最后,保存处理后的图像。
使用示例
下面是完整的代码(直接链接):
让我们试试样本图像上的代码。如果图像与demo.py
文件在同一个目录中,您可以从那里用python --images image.png
运行它:
输入图像(源)
使用 Detectron2 进行实例分割后的结果
正如你所看到的,它工作得很好!可视化工具添加了边界框(检测到的对象周围的矩形)、类名(例如“盆栽”)和以%为单位的类的模型确定性度量。即使在具有挑战性的条件下(不同的比例、彼此非常接近的物体、部分障碍物),大多数物体也能被正确地检测到,并且分割掩模相当精确。这就是那些尖端神经网络的强大之处。
摘要
在本文中,我们仅仅触及了探测器 2 的表面。这是一个庞大的库,有很多功能和技术细节,有非常复杂的类模型。我希望这能帮助你开始并更好地理解这个演示。如果你对我在这里写的东西还有疑问,或者想要关于 Detectron2 的其他文章(任何特定的主题),请在评论中告诉我。
了解 DICOM
如何阅读、书写和组织医学图像
DICOM 是在医院数据库中存储和传输医学图像的主要文件格式。
还有其他存储图像的文件格式。除了 DICOM,您还可以看到以 NIFTI 格式(文件后缀为。nii”)、PNG 或 JPEG 格式,甚至像 NumPy 数组这样的 Python 文件对象。
那么为什么要用 DICOM 呢?其他文件格式可能更方便,但在临床实践中,一切都使用 DICOM 格式。随着我的项目越来越先进,我经常发现自己重写代码来直接读写 DICOM 文件。此外,DICOM 文件是明确的,因为每个文件都包含一个文件头,详尽地记录了医院、患者、扫描仪和图像信息,就像每次拍摄 iPhone 照片时,智能手机信息甚至 GPS 都被编码在元数据中一样。
【DICOM 的建议读数
DICOM 文件格式记录在 DICOM 标准中,这是大多数信息学专家的必读文件。
对于一个初学者的背景,这个博客也是一个很好的介绍。
DICOM 的挑战
对于深度学习任务,最终目的通常是将图像数据加载为 NumPy 或其他文件,在这种情况下,DICOM 可能会很困难:
- DICOM 为每个切片保存一个文件,因此 3D 扫描可能有数百个文件
- DICOM 文件以唯一标识符(UID)命名。这使得很难从文件夹级别对文件进行排序(在某些情况下,文件名太长,以至于超过了 Windows 计算机上 256 个字符的最大值,从而导致保存/加载问题)
- 患者和医院信息嵌入在文件头中,这使得 DICOM 很难匿名
虽然混乱,但这种组织结构是 DICOM 的重要优势。稍后,我将分享一些我编写的示例 Python 代码,它们可以帮助绕过这些问题。
识别 DICOM 文件
每个 DICOM 文件都是独立的——识别文件所需的所有信息都嵌入在每个文件头中。这些信息分为 4 个层次——患者、研究、系列和实例。
- “患者”是接受检查的人
- “研究”是在某个日期和时间在医院进行的成像程序
- “系列”—每个研究由多个系列组成。一个系列可以代表在一次研究中对患者进行多次物理扫描(通常用于 MRI ),也可以是虚拟的,即对患者进行一次扫描,然后以不同的方式重建数据(通常用于 CT)
- “实例”-3D 图像的每个切片都被视为一个单独的实例。在这种情况下,“实例”与 DICOM 文件本身同义
为了说明这种层次结构,这里有一些来自癌症成像档案馆 (TCIA) 的 的公开胰腺癌数据集的文件:
图一。按“患者”、“研究”、“系列”和“实例”级别组织的 DICOM 文件示例。图片作者。
表 1 显示了使用 PyDicom (一个允许读取和写入 Dicom 文件的 python 包)打印出的标题。我删除了大部分不相关的信息,还创建了一些“虚假”的患者数据。
表 1:显示患者、系列、研究和实例 uid 和文本描述的 DICOM 标题部分的打印输出。图片作者。
从表 1 中的两个描述字段中,我们可以看到该文件来自腹部的 CT 检查,该检查使用了对比剂,这与来自胰腺癌成像数据库的该扫描相一致。这个系列“ABD”可能是唯一的,也可能有其他的。在临床 CT 研究中,通常会看到定位扫描(“拓扑结构图”)以及“矢状”和“冠状”扫描,它们在不同的平面上突出显示解剖结构。
唯一标识符:uid
除文本描述外,扫描还通过唯一的患者 ID (5553226)、研究 ID(1 . 2 . 826 . 0 . 1 . 3680043 . 2 . 1125 . 1)进行识别。38381854871216336385978062044218957),系列 UID (1.2.826.0.1。3680043 . 2 . 1 . 1)和实例号(20)。
如果您要加载该文件夹中的下一个 DICOM 文件,患者 ID、病历报告 UID 和系列图像 UID 将具有相同的值,只有实例编号不同(在本例中为“21”)。
文本描述很有帮助,但 uid 是识别扫描的关键。与描述不同,uid 对于在医院进行的每个患者、系列和研究都是独特的。
此外,uid 实际上不是随机数,它们编码了关于文件身份的信息,甚至是如何压缩的信息。UID 的完整描述在 Dicom 标准的第 6 部分中。
我见过的大多数 DICOM 病历报告都是以“三层”文件夹结构组织的,如图 1 所示,文件首先按患者 UID 排序,然后是病历报告实例 UID,然后是系列图像实例 UID,最后文件名本身就是实例编号。
结论
在这个例子中,我简要介绍了如何识别 DICOM 文件,以及如何使用这些信息对数据集进行排序。
在下一篇文章中,我将描述我写的一个 python 脚本,它可以根据文件头中的 UID 信息将一组 DICOM 文件重新组织到一个一致且易于理解的文件夹结构中。
了解 DICOMs
2020 年 9 月更新 : fast.ai 版本 2 于 2020 年 8 月正式发布。下面使用的fastai
是指最新版本,目前为2.0.9
一个 深入实践 方法,如何使用fastai的 医学成像模块查看和操作 DICOM 图像,并为机器学习做好准备。
使用像素选择和“ocean_r”色图生成的图像
什么是 DICOMs?
DICOM(DIImaging andCOcommunications inMedicine)是事实上的标准,它建立了允许医学图像(X 射线、MRI、CT)和相关信息在来自不同供应商、计算机和医院的成像设备之间交换的规则。DICOM 格式提供了一种合适的方法,该方法符合医疗信息交换 (HIE)标准,用于在医疗机构之间传输医疗相关数据,并符合 HL7 标准,该标准是使临床应用能够交换数据的消息标准。
典型的放射学工作流程[ 图像认证
DICOM 文件通常有一个 。dcm 扩展,提供了在单独的【标签】中存储数据的方法,例如患者信息、图像/像素数据、使用的机器以及更多信息(如下所述)。
DICOM 文件主要由打包成一个文件的标题和图像像素强度数据组成。标题中的信息被组织成一系列标准化的标签。通过从这些标签中提取数据,用户可以访问有关患者人口统计、研究参数等重要信息。
DICOM 的一部分[ 图像来源
16 位 DICOM 图像有从 -32768 到 32768 的值,而8 位 灰阶图像存储从 0 到 255 的值。DICOM 图像中的数值范围非常有用,因为它们与 Hounsfield 标度 相关联,Hounsfield 标度是一种用于描述放射密度的定量标度(或一种观察不同组织密度的方式——更多解释见下文)
安装要求
为了能够完成本教程,您需要在计算机上安装这些依赖项
安装说明可以在他们的 Github 页面查看: fastai
还需要安装pydicom(
Pydicom 是一个 python 包,用于解析 dicom 文件,可以很容易地将 DICOM 文件转换成 python 结构,以便于操作。
pip install pydicom
而scikit-image(
是图像处理的算法集合)
pip install scikit-image
kornia(
是一个包含操作符的包库,这些操作符可以插入到神经网络中,以训练模型来执行图像变换、核几何、深度估计和低级图像处理,如直接在张量上操作的滤波和边缘检测
pip install kornia
有关如何使用 fastai 的医学成像模块的更多信息,请访问我的
[*github*](https://github.com/asvcode/MedicalImaging)
页面 或我的 医学成像教程博客 (哪个更适合查看笔记本教程:)
数据集
这里列出了 3 个 DICOM 数据集,您可以随意使用。这 3 个数据集各有不同的属性,显示了不同 DICOM 数据集中可以包含的信息的巨大差异。
fastai
库中方便地提供了SIIM_SMALL
数据集((250 个 DICOM 文件,~30MB ),但是其某些属性受到限制,例如,它没有RescaleIntercept
或RescaleSlope
,并且其像素范围被限制在 0 到 255 的范围内- Kaggle 有一个很容易访问的(437MB) CT 医学图像数据集来自癌症成像档案馆。数据集由 100 幅图像(512 像素乘 512 像素)组成,像素范围从-2000 到+2000
- 超声数据集中的甲状腺分割提供低质量(从 253px 到 253px)的 DICOM 图像,其中每个 DICOM 图像有多个帧(平均 1000 帧)
让我们加载依赖项:
#Load the dependancies
from fastai.basics import *
from fastai.callback.all import *
from fastai.vision.all import *
from fastai.medical.imaging import *import pydicom
import seaborn as sns
matplotlib.rcParams['image.cmap'] = 'bone'
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
了解一些关于 fast.ai 的知识是有益的,超出了本教程的范围,fast.ai 文档页面有一些优秀的教程让你快速入门。
….关于数据如何存储在 DICOM 文件中的更多信息
使用pydicom.dcmread
打开 DICOM 文件,例如使用SIMM_SMALL
数据集:
#get dicom files
items = get_dicom_files(pneumothorax_source, recurse=True, folders='train')#now lets read a file:
img = items[10]
dimg = dcmread(img)
您现在可以查看 DICOM 文件中包含的所有信息。对每个元素的解释超出了本教程的范围,但是 这个 站点有一些关于每个条目的优秀信息。信息由 DICOM 标签(例如:0008,0005)或 DICOM 关键字(例如:特定字符集)列出。
比如说:
dimg
现在生成:
从“dimg”创建的“head”信息
以下是上述标签信息的一些要点:
- 像素数据 (7fe0 0010)(最后一项)——这是存储原始像素数据的地方。为每个图像平面编码的像素的顺序是从左到右、从上到下,即左上像素(标记为 1,1)首先被编码
- 光度解释 (0028,0004)——又名颜色空间。在这种情况下,是单色 2,其中像素数据被表示为单个单色图像平面,其中最小样本值被显示为黑色信息
- 每像素样本数 (0028,0002) —这应该是 1,因为该图像是单色的。例如,如果色彩空间是 RGB,这个值将是 3
- 存储的位数 (0028 0101) —每个像素样本存储的位数
- 像素表示 (0028 0103) —可以是无符号的(0)或有符号的(1)
- 有损图像压缩 (0028 2110) — 00 图像没有经过有损压缩。01 图像已经过有损压缩。
- 有损图像压缩方法(0028 2114)——说明使用的有损压缩类型(在本例中为 JPEG 有损压缩,由
CS:ISO_10918_1
表示)
未包含在SIMM_SMALL
数据集中的重要标签:
- 重新缩放截距 (0028,1052) —其中值 b 与存储值(SV)和输出单位之间的关系。输出单位= mSV + b* 。
- 重标度斜率 (0028,1053)——m在重标度截距(0028,1052)指定的方程中。
RescaleIntercept 和 RescaleSlope 用于将图像的像素值转换为对应用有意义的值。计算新值通常遵循线性公式:
- new value=(raw pixel valuerescale slope)+RescaleIntercept*
并且当关系不是线性时,利用 LUT (查找表)。
当我们在下面查看另一个数据集时,所有这些将变得更加清晰
上面的列表是有限的,使用另外两个数据集会告诉你每个文件可以有大量的标签。值得指出的是,在可能有ImageComments
的tag
的地方,可能有额外的信息(然而并不总是这样)。这个tag
可能包含对建模有用的信息。
…那像素数据呢?
默认情况下,pydicom 将像素数据作为文件中的原始字节读取,通常PixelData
通常不会立即有用,因为数据可能以各种不同的方式存储:
- 像素值可以是有符号或无符号的整数,也可以是浮点数
- 可能有多个图像帧
- 每帧可能有多个平面(即 RGB ),像素的顺序可能不同。这只是几个例子,更多信息可在 pycidom 网站上找到
这是PixelData
的样子:
dimg.PixelData[:200]
像素数据
由于解释PixelData
的复杂性, pydicom 提供了一种简单的方法来以方便的形式获得它:pixel_array
,它返回一个包含像素数据的numpy.ndarray
:
dimg.pixel_array, dimg.pixel_array.shape
像素阵列
加载每个文件有 1 帧的 DICOMs
SIIM_SMALL
数据集是一个 DICOM 数据集,其中每个 DICOM 文件都有一个包含 1 幅图像的pixel_array
。在这种情况下,fastai.medical.imaging
中的show
功能可以方便地显示图像
source = untar_data(URLs.SIIM_SMALL)
items = get_dicom_files(source)
patient1 = dcmread(items[0])
patient1.show()
patient1.show()将显示上面的图像
从CT medical image dataset
加载一个图像怎么样,每个 DICOM 文件也包含一个帧。这张图片是 CT 扫描的切片,看着肺,心脏在中间。
csource = Path('C:/PillView/NIH/data/dicoms')
citems = get_dicom_files(csource)
patient2 = dcmread(citems[0])
patient2.show()
patient2.show()
然而,如果一个 DICOM 数据集每个文件有多个帧呢?
加载每个文件有多个帧的 DICOMs
超声数据集中的甲状腺分割是一个数据集,其中每个 DICOM 文件都有多个帧。
开箱后,您无法使用fastai
查看来自多帧 DICOM 文件的图像,因为这将导致出现TypeError
,但是我们可以自定义show
功能,以便检查文件是否有多于 1 帧,如果有,您可以选择查看多少帧(默认为 1)以及打印每个文件中有多少帧。
2020 年 9 月更新:我向库提交了一个 PR,现在它是fastai.medical.imaging
模块中show
函数的一部分,所以可以开箱即用。
#updating to handle multiple frames
[@patch](http://twitter.com/patch)
[@delegates](http://twitter.com/delegates)(show_image, show_images)
def show(self:DcmDataset, frames=1, scale=True, cmap=plt.cm.bone, min_px=-1100, max_px=None, **kwargs):
px = (self.windowed(*scale) if isinstance(scale,tuple)
else self.hist_scaled(min_px=min_px,max_px=max_px,brks=scale) if isinstance(scale,(ndarray,Tensor))
else self.hist_scaled(min_px=min_px,max_px=max_px) if scale
else self.scaled_px)
if px.ndim > 2:
gh=[]
p = px.shape; print(f'{p[0]} frames per file')
for i in range(frames): u = px[i]; gh.append(u)
show_images(gh, cmap=cmap, **kwargs)
else:
print('1 frame per file')
show_image(px, cmap=cmap, **kwargs)
Fastai
有一个非常直观的方法,你可以用 修补 代码,因此有了上面的@patch
,你可以很容易地给代码添加不同的功能
现在我们可以加载甲状腺数据集了:
tsource = Path('C:/PillView/NIH/data/thyroid')
titems = get_dicom_files(tsource)
patient3 = dcmread(titems[0])
patient3.show(10)
patient3.show(十)。我们指定了 10,因此它显示了这 1 个文件中总共 932 帧中的前 10 帧
了解组织密度
使用 CT 医学图像数据集,我们现在可以尝试其他有用的fastai.medical.imaging
功能。如前所述 16 位 DICOM 图像的像素值范围从 -32768 到 32768
patient2
以上是来自该数据集的 DICOM 文件。
#lets convert the pixel_array into a tensor. Fastai can #conveniently do this for us
tensor_dicom = pixels(patient2) #convert into tensorprint(f'RescaleIntercept: {patient2.RescaleIntercept:1f}\nRescaleSlope: {patient2.RescaleSlope:1f}\nMax pixel: '
f'{tensor_dicom.max()}\nMin pixel: {tensor_dicom.min()}\nShape: {tensor_dicom.shape}')
RescaleIntercept、RescaleSlope、最大和最小像素值
该图像中RescaleIntercept
为-1024
,RescaleSlope
为1
,max
和min
像素分别为1918
和0
,图像尺寸为512
乘以512
绘制像素强度直方图,你可以看到大部分像素的位置
plt.hist(tensor_dicom.flatten(), color='c')
像素值直方图
直方图显示最小像素值为0
,最大像素值为1918
。直方图主要是双峰的,大多数像素位于0
和100
像素之间以及750
和1100
像素之间。
该图像有一个-1024
的RescaleIntercept
和一个1
的RescaleSlope
。这两个值允许将像素值转换成 Hounsfield 单位( HU ) 。在和中测量 CT 扫描上不同组织的密度
大多数 CT 扫描的范围从-1000
HUs 到+1000
HUs 其中水是0
HUs ,空气是-1000
HUs 并且组织越致密 HU 值越高。金属有一个高得多的 HU 范围+2000
HUHUs 所以对于医学成像来说一个范围-1000
到+1000
HUs 是合适的
上述像素值与组织密度不正确对应。例如,大多数像素在对应于水的像素值0
和100
之间,但是该图像主要显示充满空气的肺。亨斯菲尔德标度上的空气是-1000
胡 s
这就是RescaleIntercept
和RescaleSlope
的重要之处。Fastai2 提供了一种方便的方式scaled_px
来相对于RescaleIntercept
和RescaleSlope
重新缩放像素。
请记住:
重新调整像素=像素重新调整斜率+重新调整交点*
Fastai 再次提供了一个方便的方法scaled_px
,将pixel_array
转换为tensor
,并通过考虑RescaleIntercept
和RescaleSlope
来缩放这些值。
#convert into tensor taking RescaleIntercept and RescaleSlope into #consideration
tensor_dicom_scaled = scaled_px(patient2)
plt.hist(tensor_dicom_scaled.flatten(), color='c')
使用“scaled_px”的缩放像素直方图
现在让我们看看最大和最小像素值:
print(f'Max pixel: {tensor_dicom_scaled.max()}\nMin pixel: {tensor_dicom_scaled.min()}')
重新缩放后,最大像素值为894
,最小值为-1024
,我们现在可以根据 Hounsfield 比例正确地看到图像的哪些部分对应于身体的哪些部分。
查看直方图的顶端,值超过 300 HUs 的图像看起来像什么?
show
功能具有指定max
和min
值的能力
patient2.show(max_px=894, min_px=300, figsize=(5,5))
查看 300 像素和 894 像素之间的像素值
回想一下,我们让为修补了show
函数,现在它显示这个数据集有1 frame per file
。 HU 值高于+300
通常会显示图像内的骨骼结构
那么在-250
和250
的范围内,我们有一个峰值像素呢?
patient2.show(max_px=250, min_px=-250, figsize=(5,5))
像素范围在-250 像素和+250 像素之间
在这个范围内,你现在可以看到主动脉和心脏的部分(图片中间)以及肌肉和脂肪。
在-250 像素和-600 像素之间,我们注意到像素分布很低,怎么办
patient2.show(max_px=-250, min_px=-600, figsize=(5,5))
在这个范围内,你只能看到轮廓。直方图显示在这个范围内没有很多像素
-1000px 到-600px 之间呢?
patient2.show(max_px=-600, min_px=-1000, figsize=(5,5))
在这个范围内,你可以清楚地看到肺部的支气管
patient2.show(max_px=-900, min_px=-1024, figsize=(5,5))
在这个范围内,你现在也可以清楚地看到扫描仪的曲线。
默认情况下,show
函数的max_px
值为None
,而min_px
值为-1100
patient2.show(max_px=None, min_px=-1100, figsize=(5,5))
如上所述的图像缩放确实是为了人类的利益。计算机屏幕可以显示大约 256 种灰度,而人眼只能检测大约 6%的灰度变化,这意味着人眼只能检测大约 17 种不同的灰度。
DICOM 图像可能具有从-1000
到+1000
的宽范围,并且为了使人类能够看到图像中的相关结构,使用了windowing
过程。Fastai 提供各种dicom_windows
,因此屏幕上只显示特定的 HU 值。更多关于窗口化的内容可以在 这里找到
….关于窗口的旁注
DICOM 图像可以包含大量的像素值,而windowing
可以被视为一种处理这些值的方法,以改变图像的外观,从而突出显示特定的结构。一个窗口有两个值:
l
=窗位或中心,也称为亮度
w
=窗口宽度或范围,又名对比度
举例:从到这里
大脑物质窗口
l = 40(窗口中心)w = 80(窗口宽度)
显示的体素范围从 0 到 80
计算体素值:
- 最低可见值=窗口中心-窗口宽度/ 2
- 最高可见值=窗口中心+窗口宽度/ 2
(最低可见值= 40-(80/2),最高可见值= 40 + (80/2))
因此,所有大于 80 的值都是白色的,所有小于 0 的值都是黑色的。
….但是计算机真的关心窗口、缩放比例吗?
如果windowing
是为了人类的利益,当数据有一个均匀分布时,计算机从训练中产生更好的结果,正如本文中提到的不像放射科医生那样看
回头看看像素分布,我们可以看到图像并没有均匀分布
重新缩放像素的直方图
fastai 有一个函数freqhist_hist
,它根据您为n_bins
设置的值将像素值的范围分成多个组,这样每个组都有大约相同数量的像素。
例如,如果您将n_bins
设置为 1,像素值将被分成两个不同的像素箱。
ten_freq = tensor_dicom_scaled.freqhist_bins(n_bins=1)
fh = patient2.hist_scaled(ten_freq)
plt.hist(ten_freq.flatten(), color='c'); show_image(fh, figsize=(7,7))
将像素分成两个不同的箱
在这种情况下,您可以在-1000
处看到图像的两个极边,在处看到空气部分,在500
处处可以清楚地看到骨骼结构,但这种分布对于机器学习模型来说仍不完全可接受。
n_bins
在100
(这是show
使用的默认数字)
分布在 100 个箱内的像素
那么在n_bins
处100000
的像素显示出更均匀的分布呢
分布在 100000 个箱上的像素
这对训练结果有什么影响。这将是下一篇教程的主题。
参考资料:
- https://www . himss . org/inter operability-and-health-information-exchange
- https://www.hl7.org/implement/standards/
- https://en.wikipedia.org/wiki/Hounsfield_scale
- https://github.com/fastai/fastai2
- https://www . ka ggle . com/jhoward/don-t-see-like-a-放射科医生-fastai/data
- https://www . ka ggle . com/DC stang/see-like-a-a-放射科医生-系统开窗
新老德雷克歌词与人工智能
使用文本分类和 LSTM 模型理解德雷克的老歌和新歌之间的差异。
德鲁·比默|https://unsplash.com/photos/Kxc7dgkjdNo
德雷克的歌词随着时间的推移有变化吗?如果是这样,那怎么做?
我用机器学习识别老德雷克歌词和新德雷克歌词,准确率 86%!这说明新旧德雷克歌曲歌词是有区别的。现在,让我们找出这些差异,并测试模型预测哪些歌曲是新旧德雷克歌曲。
- ** 如果对过程和结果不感兴趣,请跳到第 5 和第 6 节。
本文共分 7 节:
1)数据探索、清理和处理
2)使用计数矢量器的 ML 建模
3)使用 Tfidf 矢量器的 ML 建模
4)对 2 和 3 中的顶级型号进行参数调整
5)分析来自模型的前 40 个特征,以发现新旧德雷克之间的差异。
6)让我们通过预测单曲和 mixtape 歌曲是旧的还是新的 Drake 来看看模型的运行情况。
7)使用 LSTM 的序列建模
1)数据预处理
我用 Genius API 收集了德雷克歌词的数据集。我只收录他官方专辑中的歌曲。因此没有单打和功能
这些数据不需要太多争论。我只是将所有文本转换成小写,并删除了字母表和数字之外的任何字符。
新德雷克的歌是 2014 年以后的歌,老德雷克的歌是 2014 年以前的。出现这种情况的两个原因是:
1)大家普遍认为德雷克最好的项目是在 2014 年之前。
- 2014 年是一个很好的中间点,为每个类别提供了一个相当平衡的数据集。
我还执行随机抽样,并创建一个更加平衡的数据集。我将展示使用两个数据集的结果。
*#Creating column to define new drake vs old drake*
*#1 represents new drake, 0 represents old drake*
df['drake'] = np.where(df['year'] > 2014, 1, 0)
在这里你可以看到德雷克专辑和歌曲的分布
sns.countplot(y=df['album'].values, order=df['album'].value_counts(ascending=**True**).index)
plt.title('# Songs Per Album')
plt.xlabel('Number Of Songs', fontsize=12)
plt.show()
数据准备阶段:
*#Tokenizing Dataset*
**from** **nltk.tokenize** **import** word_tokenize
df['lyrics'] = df['lyrics'].apply(word_tokenize)*#Stop word removal*
**from** **nltk.corpus** **import** stopwords **from** **collections** **import** Counter stop = stopwords.words('english') *#I keep words in this list as I feel they are useful in predicting drake songs* *#Furthermore, I tested accuracy with and without stop words. This setup is the best* remove_stop = ['i', 'me', 'myslef', 'we', 'you', 'we', 'she', 'her', 'they'] stop = list((Counter(stop)-Counter(remove_stop)).elements()) df['lyrics'] = df['lyrics'].apply(**lambda** x: [item **for** item **in** x **if** item **not** **in** stop])*#Lemitization*
**from** **nltk.stem** **import** WordNetLemmatizer
**def** lemmatize_text(text):
lemmatizer = WordNetLemmatizer()
**return** [lemmatizer.lemmatize(w) **for** w **in** text]df['lyrics'] = df['lyrics'].apply(lemmatize_text)
接下来,我将数据集分为训练和测试,其中 80%用于训练,20%用于测试。此外,我还将使用 K-fold 验证来测试准确性,因为它对于像这样的较小数据集更准确。
*#Defining X and Y*
X = df['lyrics']
y = df['drake']*## Divide the dataset into Train and Test*
**from** **sklearn.model_selection** **import** train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size= 0.2, random_state = **None**)
2)使用计数矢量器的 ML 建模
现在是时候用计数矢量器测试模型了。计数矢量器计算文本中的词频,并用词频创建一个数组。
*# Applying Countvectorizer
# Creating the Bag of Words model*
*#I tried different n_gram ranges and unigram works best*
**from** **sklearn.feature_extraction.text** **import** CountVectorizer
cv = CountVectorizer()
cv.fit(X_train)
trainx_cv = cv.transform(X_train)
testx_cv = cv.transform(X_test)#For cross validation I create a new count vectorizer and use it in a sklearn pipeline with the model.#Example pipeline.
mnb = Pipeline([('vect', CountVectorizer()), ('mnb', MultinomialNB())])
cvs = cross_val_score(mnb, X, y)
print("Accuracy: %0.2f (+/- %0.2f)" % (cvs.mean(), cvs.std() * 2))# The pipeline will do feature extraction in each fold separately and prevent leakage.
接下来,我为文本分类创建了以下模型:
注意:我使用训练/测试/分割和交叉验证来测试模型。以下是交叉验证准确性,因为它们对于像这样的较小数据集更准确。
为了节省空间,我不包括这些代码。我的 Github 上有它们的代码。
3)使用 TF-IDF 矢量器的 ML 建模
接下来,我重复相同的步骤,但是这次我使用 TF-IDF 矢量器,而不是计数矢量器。TF-IDF 做的事情与 count vectorizer 相同,但是值与 count 成比例增加,与单词的频率成反比。
#Lets try with Tf-IDF
#Steps are repeated as before
#I tried different n_gram ranges and unigram works best
from sklearn.feature_extraction.text import TfidfVectorizer
tf=TfidfVectorizer()
tf.fit(X_train)
trainx_tf = tf.transform(X_train).toarray()
testx_tf = tf.transform(X_test).toarray()#For cross validation I create a new tfidf vectorizer and use it in a sklearn pipeline with the model.#Example pipeline.
mnb = Pipeline([('vect', TfidfVectorizer()), ('mnb', MultinomialNB())])
cvs = cross_val_score(mnb, X, y)
print("Accuracy: %0.2f (+/- %0.2f)" % (cvs.mean(), cvs.std() * 2))# The pipeline will do feature extraction in each fold separately and prevent leakage.
TF-IDF 矢量器的精度为:
4)参数调整
好了,现在我们知道朴素贝叶斯使用计数矢量器的性能最好,线性 SVC 使用 TF-IDF 矢量器的性能最好。现在让我们尝试进一步优化模型参数:
#Multinomial NB tunning
pipeline = Pipeline([
('vect', CountVectorizer()),
('classifier', MultinomialNB()),
])parameters = {
'vect__max_df': (0.5, 0.75, 1.0),
'vect__min_df': (1,2,3),
'vect__max_features': (None, 5000, 10000,15000),
'vect__ngram_range': ((1, 1), (1, 2)), # unigrams or bigrams
'classifier__alpha': (1, 0.1, 0.01, 0.001, 0.0001, 0.00001),
}
Best_NB = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1)
Best_NB.fit(X_train, y_train)
best_parameters = Best_NB.best_estimator_.get_params()
在相同的测试数据集上比较调优前后的准确性。
请记住,前面几节中的精度是交叉验证精度。在这里,我比较了同一测试数据集上的火车测试分割精度,以查看相同数据上的性能的直接比较。
#LinearSVC tunning
pipeline = Pipeline([
('tfidf', TfidfVectorizer()),
('clf', LinearSVC()),
])parameters = {
'tfidf__max_df': (0.90, 1.0),
'tfidf__min_df': (1,2,3,),
'tfidf__ngram_range': ((1, 1), (1, 2)), # unigrams or bigrams
'clf__C': (0.1, 1, 10, 100, 1000),
'clf__penalty': ('l1', 'l2'),
}
Best_SVC = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1)
Best_SVC.fit(X_train, y_train)
best_parameters = Best_SVC.best_estimator_.get_params()
总体而言,在平衡数据集上训练的模型表现稍好。后来,当我对一些新数据进行测试时,我注意到在原始数据集上训练的模型对老歌略有过度拟合,因为它们完美地预测了老歌,但新歌的准确性不如在平衡数据集上训练的模型。在平衡数据集上训练的模型在老歌预测准确性方面略有下降,可能是因为通过移除 30%的数据集而遗漏了一些重要的老歌特征。因此,将来当德雷克推出另一张专辑时,我会用更多的数据重新测试它。
所以现在我们有超过 80%准确率的模型。这表明,有一些特征有助于成功地区分德雷克歌曲是新的还是旧的德雷克。现在让我们来看看新德雷克和老德雷克的不同之处。
5)特征重要性
为此,我将查看模型系数值。此外,我只分析顶级模型。
*#Thanks to tobique for the method. Link: https://stackoverflow.com/questions/11116697/how-to-get-most-informative-features-for-scikit-learn-classifiers*
*#This method look at the coeficient values and orders them based on most negative and positive*
*#The most positive values link to words defining old drake song*
*#The most negative values link to words defining new drake song*
**def** show_most_informative_features(vectorizer, clf, n=20):
feature_names = vectorizer.get_feature_names()
coefs_with_fns = sorted(zip(clf.coef_[0], feature_names))
top = zip(coefs_with_fns[:n], coefs_with_fns[:-(n + 1):-1])
**for** (coef_1, fn_1), (coef_2, fn_2) **in** top:
print ("**\t%.4f\t%-15s\t\t%.4f\t%-15s**" % (coef_1, fn_1, coef_2, fn_2))
正如《代码》中所提到的,这种方法的所有功劳都归 Tobique 所有。这种方法查看模型系数,然后根据最大负值和最大正值对它们进行排序。它还将系数映射到特性名称,这使我们很容易理解它们。在我们的例子中,最负的系数与老德雷克歌曲歌词相关,最正的系数与新德雷克歌曲歌词相关。以下是两款车型的 40 大特色:
老德雷克关键词:嗯,布特,嗯,钱,美元,汽车,嚯,锄头,婊子,女孩,爱情,她,她,你,爱过,错过,低,品牌,褪色,梦想,球,船员。
新德雷克关键词:耶,伊,哇,仍然,不,妈妈,传道,祈祷,上帝,工作,轮班,奉献,妻子,感觉,孤独,宝贝,宝贝,六,侧,婴儿床。
要注意的第一个变化是,老德雷克通常使用短语 uh,bout,huh,ho,而新德雷克使用 ayy,woah,nah 来代替。
老德雷克谈论更多的是爱情/关系,女人,金钱/汽车/品牌等物质化的东西,他的船员(朋友),被淡化(high)。
新德雷克已经不再谈论女性、人际关系和唯物主义,现在他在唱/说唱各种不同的东西,包括上帝/祈祷、他的母亲、多伦多(又名六人组)、工作和他的感受。
最后,德雷克还改变了他谈论女性的方式。在他以前的歌曲中,他使用了 b*tch,hoes 和 girl,而 new Drake 使用 babe,baby 和 wifey。
因此,随着德雷克作为一个人的成熟,他的歌词也成熟了。此外,他的新歌词似乎也更加多样化,针对更多的观众。
6)预测单曲/混音歌曲
现在,让我们用更多的歌曲来测试最好的模型。我从他的单曲和最新的 mixtape Dark Lane 样带中收集了更多的歌词。我收集的数据包括 14 首歌,7 首新歌和 7 首老歌。在这里,您可以看到模型准确预测了哪些歌曲。
错误的预测用红色表示。
在这个数据集上,朴素贝叶斯和线性 SVC 模型都获得了 12/14 的正确预测!!!
线性 SVC 认为欲望是一首古老的德雷克歌曲,而朴素贝叶斯模型认为信任问题是一首新的德雷克歌曲。你认为欲望听起来像老德雷克,信任问题听起来像新德雷克吗?
另一个值得注意的有趣的点是,两个模型都预测芝加哥自由泳是一首老歌,而它是一首新的德雷克歌曲。你觉得芝加哥自由式听起来像老德雷克吗?
该项目已成功识别德雷克的老歌风格和新歌风格的变化。
如果有你想让我测试的歌曲,请告诉我。
7)LSTM 序列模型
最后,对于那些感兴趣的人,我还使用 LSTM 进行了序列分析,看看在特定序列中使用的单词是否能区分新旧德雷克。为此,我进一步将歌词分成 4462 行单行句子,看看德雷克的新旧歌词中是否有序列。然而,LSTM 模型给出了 60%的准确率。
x = df_final['lyrics'].values y = df_final['drake'].values x_train, x_test, y_train, y_test = \ train_test_split(x, y, test_size=0.2, random_state=**None**)tokenizer = Tokenizer(num_words=100) tokenizer.fit_on_texts(x) xtrain= tokenizer.texts_to_sequences(x_train) xtest= tokenizer.texts_to_sequences(x_test)vocab_size=len(tokenizer.word_index)+1maxlen=15 xtrain=pad_sequences(xtrain,padding='post', maxlen=maxlen) xtest=pad_sequences(xtest,padding='post', maxlen=maxlen)embedding_dim=50
model=Sequential()
model.add(layers.Embedding(input_dim=vocab_size,
output_dim=embedding_dim,
input_length=maxlen))
model.add(layers.LSTM(units=50,return_sequences=**True**))
model.add(layers.LSTM(units=10))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(8))
model.add(layers.Dense(1, activation="sigmoid"))
model.compile(optimizer="adam", loss="binary_crossentropy",
metrics=['accuracy'])
model.summary()model.fit(xtrain,y_train, epochs=20, batch_size=16, verbose=**False**)
loss, acc = model.evaluate(xtrain, y_train, verbose=**False**)
print("Training Accuracy: " + str(acc))loss, acc = model.evaluate(xtest, y_test, verbose=**False**)
print("Test Accuracy: " + str(acc)
因此,我不认为老德雷克和新德雷克歌词之间有太多的顺序关系。这种差异归结于单词的使用,而不是它们的使用顺序。
感谢您的阅读
请随时留下任何反馈和建议。
我的下一个项目是理解新旧说唱歌曲的区别。敬请关注。
笔记本:https://github.com/HamzaKazmi/Drake_Lyrics_Analysis
这个项目的灵感来自鲁斯兰的工作,他使用 LSTM 生成德雷克歌词。https://towards data science . com/generating-drake-rap-lyrics-using-language-models-and-lstms-8725 d71b 12
使用 R 理解发行版
R 中的分布基础是数据可视化的先决条件
你如何传达数据驱动的发现?
数据可视化!
有各种各样的工具可以实现这一点,本文将介绍其中的一个工具——r。
但是在我们开始使用 R 可视化数据之前,我们需要理解一些概念和术语。在这篇文章结束时,我希望你能够理解:
a)分布以及如何使用它们来汇总您的数据集
b)直方图和密度图之间的差异
c)正态分布&使用标准单位
d)如何使用分位数图检查正态分布
e)何时以及如何使用箱线图
这里我们介绍一个问题。假设有一个来自另一个星球的霸主,我们需要向他描述身高,比如说,一群学生。为此,我们首先需要收集学生的性别和身高数据。向外星霸主解释数据集的一种方式是简单地与它共享数据集。
展示这些数据的另一个更简单的方法是通过分布——所有列表中最基本的统计汇总。在描述一系列分类变量或连续变量时,会用到不同形式的分布。例如,在这种情况下,可以使用 prop.table()命令对变量“sex”进行汇总,以生成频率表,并帮助我们理解给定数据集中男性和女性两个类别的比例。
library(dslabs)
data(heights)
# make a table of category proportions
prop.table(table(heights$sex))
有了更多的类别,我们可以利用条形图来描述分布。但是对于数字数据,绘制分布图可能更具挑战性。这是因为单个数据点可能不是唯一的。一个人可能报告他/她的身高为 68.897 英寸,而另一个人可能报告同样的身高为 68.503 英寸。很明显这些人是分别从 175 和 174 英寸换算出来的身高。
这种条目的表示需要分布函数。这就是“累积分布函数”概念发挥作用的地方。随机变量 X 的 CDF 定义为,
让我们假设 X 是一个离散随机变量,范围 R = {x1,x2,x3…}并且范围 R 从下面有界(即 x1)。下图显示了生成的 CDF 的一般形式。CDF 是一个非减函数,当 x 变得足够大时,它趋近于 1。
来源:https://www.probabilitycourse.com/chapter3/3_2_1_cdf.php
柱状图
回到高度的数据集示例,当我们为它构建 CDF 时,我们需要评估它是否回答了重要的问题,如分布集中在哪里,哪个范围包含大多数数据,等等。虽然 CDF 可以帮助回答这类问题,但它要困难得多。
相比之下,我们现在利用直方图来更好地理解数据。只要看一眼剧情,霸王就能从中推断出重要的信息。
我们可以在 69 英寸左右对称分布,大部分数据位于 63 到 74 英寸之间。有了这种近似,我们总是会丢失一些信息。在这种特殊情况下,影响可以忽略不计。
密度图
我们还可以利用平滑的密度图来显示数据的分布。在平滑噪声的同时,图中的峰值有助于识别大部分值集中的区域或范围。
密度图优于直方图的一个主要优点是,它们更能确定分布的形状。例如,具有比如说 5 个面元的直方图不会像 15 个面元的直方图那样产生可区分的形状。然而,在使用密度图时,不存在这样的问题。
正态分布
让我们更进一步,计算这个数据集的平均值和平均偏差。我们都熟悉正态分布的含义。当您从独立来源的随机数据集合中绘制点时,它会生成一条钟形曲线(或高斯曲线)。在这个图表中,曲线的中心将给出数据集的平均值。
以下是 R 中用于生成正态分布函数的内置函数:
- dnorm() —用于针对给定的平均值和标准差,找出每个点的概率分布的高度。
x <- seq(-20, 20, by = .1)
y <- dnorm(x, mean = 5, sd = 0.5)
plot(x,y)
2。pnorm() —也称为“累积分布函数”(CDF),pnorm 用于找出正态分布随机数小于给定数值的概率。
x <- seq(-10,10,by = .2)
y <- pnorm(x, mean = 2.5, sd = 2)
plot(x,y)
3。qnorm() —获取概率值并给出一个数字,其累积值与概率值匹配。
x <- seq(0, 1, by = 0.02)
y <- qnorm(x, mean = 2, sd = 1)
plot(x,y)
4。rnorm() —该函数用于生成正态分布的随机数。
y <- rnorm(50)
hist(y, main = "Normal DIstribution")
正态分布方程:
# define x as vector of male heights
library(tidyverse)
library(dslabs)
data(heights)
index <- heights$sex=="Male"
x <- heights$height[index]# calculate the mean and standard deviation manually
average <- sum(x)/length(x)
SD <- sqrt(sum((x - average)^2)/length(x))# built-in mean and sd functions
average <- mean(x)
SD <- sd(x)
c(average = average, SD = SD)
在数据集高度的情况下,这种分布以平均值为中心,大多数数据点在平均值的两个标准偏差范围内。我们观察到这种分布仅由两个参数定义——均值和标准差,因此这意味着如果数据集遵循正态分布,则可以用这两个值来概括。
在 R 中,我们利用函数标度来获得标准单位。数学上,标准单位定义如下:
它基本上告诉我们一个物体 x(在这种情况下是高度)偏离平均值的标准偏差的数量。
# calculate standard units
z <- scale(x)# calculate proportion of values within 2 SD of mean
mean(abs(z) < 2)
平均值的-2 和+2 圈内的值的比例约为 95%,这正是正态分布的预测值。
但是我们如何检查这些分布是否是正态分布的近似值呢?
分位数—分位数图
这里主要概念是,我们定义一系列比例 p,并基于定义的分位数 q,使得数据中低于 q 的值的比例是 p。因此,如果数据的分位数等于正态分布的分位数,我们可以得出数据近似正态分布的结论。
现在让我们计算样本和理论分位数,以检查数据点是否落在同一直线上。
# calculate observed and theoretical quantiles
p <- seq(0.05, 0.95, 0.05)
observed_quantiles <- quantile(x, p)
theoretical_quantiles <- qnorm(p, mean = mean(x), sd = sd(x))# make QQ-plot
plot(theoretical_quantiles, observed_quantiles)
abline(0,1)
事实上,这些值落在单位线上,这意味着这些分布很好地近似为正态分布。
注:
a)分位数的特例,百分位数是定义 p = 0.01,0.02,0.03…时得到的分位数。,0.99
b)四分位数是第 25、50 和 75 个百分位数,第 50 个百分位数给出了中值。
箱线图
如果数据集不符合正态分布,并且两个参数(均值和标准差)不足以汇总数据,会发生什么情况?箱线图在这种情况下会很有帮助。将数据集分为三个四分位数,箱线图表示数据集中的第一个四分位数、第三个四分位数、最小值、最大值和中值。
让我们使用相同的“身高”数据集来创建学生性别(男/女)和身高之间关系的基本箱线图。
data(heights)
boxplot(height~sex, data=heights, xlab="Sex",ylab="height")
从上面的图中,我们可以推断出两组的标准偏差几乎是相似的,尽管平均来看,平均比女性高。
如果你想进一步了解测试统计中其他/不常见的分布,请参考 R Stats 包中的‘分布’(下面给出的链接)
[## 分布
分布在统计包密度,累积分布函数,分位数函数和随机变量…
www.rdocumentation.org](https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/Distributions)
理解动态编程
流行优化技术的直观指南。
作者图片
动态编程,或称 DP,是一种优化技术。它被用于多个领域,尽管本文主要关注它在算法和计算机编程领域的应用。这是算法面试中经常被问到的话题。
由于 DP 不是很直观,大多数人(包括我自己!)经常发现将问题建模为动态编程模型很棘手。在这篇文章中,我们将讨论什么时候使用 DP,然后讨论它的类型,最后通过一个例子。
目录
**When is DP used?
** - Overlapping Sub-problems
- Optimal Substructure**The Two kinds of DP
** - The top-down approach
- The bottom-up approach**An example
** - The Problem
- The analysis
- A recursive Solution
- The base case
- A dynamic programming approach
- Improving the Algorithm
什么时候使用 DP?
要使 DP 工作,问题必须满足两个必要条件。
- 重叠子问题
- 最优子结构
让我们更详细地看一下这些。
重叠子问题
这个属性正是它听起来的样子:重复的子问题。但是为了使这个有意义,我们需要知道什么是子问题。
一个子问题只是手头问题的一个较小版本。在大多数情况下,这意味着传递给递归函数的参数值更小。
如果你在找一本书的某一页,你会怎么做?你可以把书翻到某一页,然后把你所在的页码和你要找的页码进行比较。
如果当前页面比要求的页面小,您将开始在当前页面和最后一页之间查找。另一方面,如果当前页码更大,您将开始在书的开头和当前页面之间搜索。
你会继续下去,直到你找到那一页。
如果你必须把它建模成一个递归函数,那会是什么样子?可能是这样的。
注: 以下代码片段以伪代码的形式编写,以提高可读性
非常简单。有一个 getpage 函数返回我们正在寻找的页面( target_page ,此处)。该函数查看从 _ 页 到 _ 页 到 _ 页 之间的中间页,并检查是否匹配。
否则,该函数会查看我们正在查看的部分的左半部分或右半部分。
但是那两个对 getpage 的递归调用代表什么呢?您会注意到,在每次递归调用时,我们都将搜索空间减少了一半。我们现在做的是解决同一个问题,就是在更小的空间里,寻找一个特定的页面。我们在解决子问题。
分而治之,或 DAC 算法通过子问题的原理工作。“划分”部分指的是将一个问题分成子问题。像 mergesort 和 quicksort 这样的排序算法就是很好的例子。请注意,二分搜索法并不完全是 DAC 算法,原因很简单,它没有“合并”步骤,而实际的分治算法会合并其子问题的结果,以获得最终的解决方案。
既然我们已经回答了什么是子问题的问题,我们继续讨论另一个词:“重叠”。
当这些子问题需要解决不止一次时,就称之为重叠问题。查看调用图,计算第 n 个斐波那契项的值。
递归关系是:
the relation **f(n) = f(n - 1) + f(n-2)**the base case
**f(0) = 0
f(1) = 1**
递归斐波纳契调用树。f(n)是第 n 个斐波那契数——作者使用 draw.io 创建的图像。
这些调用用阴影表示重叠的子问题。相比之下,二分搜索法的子问题并不重叠。
最优子结构性质
最优子结构属性稍微复杂一些:它指的是在计算总体最优解时可以直接考虑子问题的最优解的场景。
举个简单的例子?说你要找从 A 到 B 的最短路径。设 X 为 A 和 B 之间的中间点,用单边将其连接到。
使用中间节点寻找最短路径—作者使用 draw.io 创建的图像。
为了解决这个问题,我们可以找到从所有中间节点( X )到 B 的最短路径,然后找到从 A 到 X 的路径加上从 X 到 B 的最短路径,这是所有 X 中最短的。
*shortest(A, B) = min(AX + shortest(X, B)) for all intermediate nodes X.*
我们在这里做的是使用一个最优的中间解( 【最短(X,B)】)并使用它(而不是考虑子问题的每个解)来找到最终的最优答案。
两种 DP
自上而下(记忆化)方法
在自上而下的方法中,我们从问题的最高层开始。在这种方法中,我们首先检查是否已经解决了当前的子问题。如果有,我们就返回那个值。如果没有,我们解决子问题。我们使用递归调用来解决我们的子问题。
由于这些调用需要解决我们以前没有见过的更小的子问题,我们继续这样做,直到我们遇到一个我们已经解决或知道答案的子问题。
自下而上(制表)的方法
在这种方法中,我们从最底层开始,然后一路向上。因为我们从“基本情况”开始,并使用我们的递归关系,我们真的不需要递归,所以,这种方法是迭代的。
这两种方法的主要区别在于自底向上计算所有的解,而自顶向下只计算那些需要的解。例如,为了找到源和目的地之间的最短路径,使用自顶向下的方法,我们只计算最短路径附近的中间点的距离,在每个阶段选择最小值。
另一方面,在自底向上的方法中,我们最终计算网格上每个点和目的地之间的最短距离,最终返回从起点到终点的最短距离。
作为比较,让我们看一个可能的自顶向下和自底向上的函数,它返回第 n 个斐波那契项。
C++中自顶向下的动态编程解决方案
自底向上的动态规划解决方案
虽然这两种方法具有相同的渐近时间复杂度,但是自顶向下实现中的递归调用可能导致堆栈溢出,由于自底向上方法的迭代性质,这不是问题。
请记住,尽管我们迭代地实现了后者,但您的逻辑仍然会使用非常基本的递归方法中的递归关系,正如我们将在本例中看到的那样。
一个例子
让我们来看一个问题,我们将使用两种动态编程方法来解决这个问题。
问题是
找出一个数组中元素的最大和,确保不包括相邻的元素。让我们假设没有元素是负的。
***example 1:
[1, 2, 3] => 1 + 3 = 4****example 2:
[1, 1, 1, 1] => 1 + 1 = 2****example 3:
[2, 5, 2] => 5 = 5***
分析
首先,让我们试试贪婪的方法。
因为我们的目标是最大化我们选择的元素的总和,我们可以希望通过选择最大的元素,忽略它的邻居,然后继续这样做来实现这一点。在这里,我们确保每一步都有一个最大值。但是,这只有在局部情况下才是正确的,当然,我们正在寻找全球性的解决办法。
这种方法在某些情况下是可行的。
***[1, 5, 1, 10, 1, 5, 1]***
这里,我们首先选择 10,因为它是最大的元素。然后我们忽略它的邻居,这样我们就不会违反不允许选择相邻元素的条件。
接下来,我们选择两个 5,因为它们是下一个最大的元素,然后忽略它们的邻居。我们的算法到此结束,因为没有任何元素了。我们得到的结果——10+5+5——实际上是正确的答案。
但是这并不总是有效的。举以下例子:
***[1, 1, 9, 10, 9, 1, 1]***
在每一步,如果你选择了最大的元素,忽略了它的邻居,并继续这样下去,你最终会选择 10,然后 1,然后在忽略了两个 9 之后再次选择 1,这样加起来就是 12,但正确的答案应该是 1 + 9 + 9 + 1,也就是 20。
很明显,这种方法是不正确的。让我们从一个基本的递归解决方案开始,逐步发展到一个使用动态编程的解决方案。
这就是贪婪和动态编程方法之间的区别。贪婪方法关注的是尽最大努力在每一步实现目标,而 DP 则着眼于全局。与 DP 不同,使用贪婪的方法,不能保证最终会得到最优解。贪婪算法经常陷入局部最大值,导致次优解。
递归解
稍微思考一下,你可能会发现我们有一个条件需要记住:没有相邻元素。你可能会发现:
- 我们可以选择考虑求和中的一个元素,也可以忽略它
- 如果我们考虑它,我们将不得不忽略它的相邻元素
为了简洁起见,让 f(a..b) 代表调用到 f 我们的数组从索引 a 到索引 b (含两者)。那个函数 f 将代表我们的递归函数,它将解决这个问题。
所以 f(0..4) 表示从索引 0 到索引 4 运行该功能。
我们的函数调用表示—作者使用 draw.io 创建的图像。
从一个单元格指向的两个箭头代表我们对后续函数调用的选择。因为这是一个最大化问题,我们必须从这些选项中选择最大值。
让我们回到我们的数组。
***[5, 10, 100, 10, 5]***
记住上面讨论的条件,让我们实际写下我们将要做的事情。
我们的第一个调用将在整个数组上进行,如上所示,数组长度为 5。
***f(0..4)***
对于索引为 0 的元素(这里恰好是 5),我们可以选择:
- 将其包含在我们的总和中:我们当前的总和将是 5 +数组其余部分的最大总和,但不包括下一个元素(索引 1)。这样,我们的和就变成了 5 + f(2..4) 。或者概括一下, arr[0] + f(2..4)
- 排除它:我们当前的和将正好等于剩余数组的最大和。这可以写成: 0 + f(1..4) 。请注意,我们的下一个调用来自索引 1,而不是上一个案例中的 2。因为我们不考虑索引 0 处的元素,所以我们可以自由地考虑索引 1 处的元素——我们不会被迫忽略它。
我们函数的最初几次调用——作者使用 draw.io 创建的图像。
这里的图表直观地解释了这一点。如前所述,给定级别的所有箭头代表我们的选择,我们从中选择最大的一个。
所以我们最终的答案是:
***f(0..4) = max(arr[0] + f(2..4), f(1..4))***
让我们在下一次迭代中对此进行扩展。
首先,我们将为左边的树做这件事,它是 f(2..4)。这就像我们第一次调用 f 时做的一样,记住 arr[0] + 部分还在。它将被加到 f 的值(2..4) 在返回调用树的途中。
我们的选择:
- 考虑 arr2 在我们的 sum: 我们这个阶段的 sum 就变成了 arr2 + f(4..4) 。记住,因为我们考虑的是索引 2 处的元素,所以我们必须忽略下一个元素——索引 3。
- 忽略 arr2 : 我们这里的 sum 与剩余数组的最大结果相同,无需忽略相邻元素。所以,那就是 f(3..4) 。
调用树的第三层——作者使用 draw.io 创建的图像。
就像以前一样,的值为 f(2..4) 将是我们两个选择中的最大值。
***f(2..4) = max(arr[2] + f(4..4), f(3..4))***
基地案例
你认为 f(4..4) 会评价到什么?按照我们的符号,它是我们对从索引 4 到索引 4 的数组进行函数调用的结果。这意味着我们在单个元素上调用函数。单个元素的最大和就是它本身。
另一件要记住的事情是:在 f(a..b) ,a 永远不应该大于 b。因为这个调用表示从索引 a 开始,一直到索引 b,所以如果比大,我们就必须返回 0。没有元素就没有最大和。**
我们在这里有我们的基本情况。我们的函数 f 在单个元素上调用时,会直接返回该元素,如果不在有效范围内,则返回 0。没有进一步的递归调用。这就是为什么它被称为基础案例。
在我们的例子中,我们把称为 f(3..4)** 导致对 f 的无效调用(5..4) ,我们通过返回 0 来处理。我们稍后将概括这一点。**
****f(4..4) = arr[4]
f(5..4) = 0****
递归关系
让我们再看看我们的结果。
**first call: **f(0..4) = max(arr[0] + f(2..4), f(1..4))**second call:
**f(2..4) = max(arr[2] + f(4..4), f(3..4))**the base case:
**f(4..4) = arr[4]
f(5..4) = 0****
注意到前两个结果中的模式了吗?如果我们概括这些,我们得到:
****f(a..b) = max(arr[a] + f(a+2 .. b), f(a+1, b))****
这还不是我们关系的最简化版本。注意这里出现的 b 。实际上,回过头来看看我们在前一个块中的具体调用。
他们不会改变。没有 b + 1 或 b + 2 。总是 b 。而我们第一次调用中的 b 的值是多少?最后一个索引。由于 b 在我们整个算法中是常数,所以可以去掉。
我们的递归关系变成:
****f(a) = max(arr[a] + f(a+2), f(a+1))****
其中 f(a) 是从索引 a 起对数组的调用。
另一件要意识到的事情是,类似于我们如何移除b,因为它总是等于数组中的最后一个索引,引用单个元素的基本情况只有在该元素是数组中的最后一个元素时才会发生。
我们基本情况的概括版本是:
*****f(n-1) = arr[n-1]** where **n** is the size of the array
**f(a) = 0** if **a** >= **n** where **n** is the size of the array***
因此,我们有我们的关系:
*****f(a) = max(arr[a] + f(a+2), f(a+1))
f(n-1) = arr[n-1]** where **n** is the size of the array
**f(a) = 0** if **a** >= **n** where **n** is the size of the array***
让我们基于这个关系实现递归方法。
这个函数可以这样调用:
*****array := [1, 5, 2, 4, ...]
return f(array, 0)*****
这会有多复杂?
如果我们根据数组的大小( n )来估算复杂度,我们会得到这样的结果:
****T(n) = T(n-2) + T(n-1) + O(1)****T(0) = O(1)****
直观地说,对大小为 n 的数组 f 的每个调用(表示为【T(n)】)都会导致对大小为 n-2 和 n-1 的数组 f 的两个调用。也就是说,在每个阶段,我们对 f 的调用次数都会翻倍。
渐近时间复杂度是指数级的。用上面的推理,我们得到o(2^n).******
这是对上限的一个宽松估计,因为 n-2 树必然会在 n-1 树之前结束,所以我们做的比加倍调用略少。实际的复杂度是o(phin)—φ***是黄金比例—或者***【o(1.618n】,*** 比我们原来的估计略小,但是还是坚持 O(2^n) 。***
另一件要注意的事情是,上面的递归关系类似于第 n 个斐波那契项,因此会给出类似的复杂性。
动态规划方法
这就是动态编程发挥作用的地方。
注意调用图中重复出现的子问题——作者使用 draw.io 创建的图像。
如果你仔细观察,你会看到我们之前谈到的重叠的子问题。
现在到了重要的部分——将这种递归实现转换成动态编程方法。如果我们存储正在重复的函数调用的值会怎么样?
让我们维护一个数组,其中第 I 个元素是 f(i) 的值,依次是从索引 i 到结尾的数组的最大和。
****dp[i] = f(i..n) = f(i)****
因为我们已经有了 f(i)的结果,
****dp[i] = max(arr[i] + f(i + 2), f(i + 1))****
现在我们有了这个关系,我们可以走两条不同的路。要么我们走自顶向下的路线,在这里我们的函数仍然是递归的,就像上面的结果一样,要么我们去掉所有的递归调用,走自底向上的路线。
我们将关注自底向上的路线,但是让我们讨论自顶向下的方法。
-自上而下的方法
看看我们之前的结果。
****dp[i] = max(arr[i] + f(i + 2), f(i + 1))****
这就是我们实现自顶向下方法所需要的。对于任何对 f 的调用,我们将首先在我们的数组 dp 中检查我们之前是否已经进行了那个调用,如果已经进行了,我们将直接使用预先计算的值。
另一方面,如果我们正在进行的调用以前从未做过,我们必须计算整个事情。在这种情况下,一旦我们得到一个值,我们确保将它存储在我们的数组 dp 中,这样我们就不必重复整个过程。
调用树应该如下所示:
自顶向下动态编程方法中的调用树—作者使用 draw.io 创建的图像。
让我们实现这个算法。
存储子问题结果所需的额外空间随着输入数组的大小而线性增长。因此,除了递归堆栈所需的 O(n) 空间之外,我们还有一个 O(n) 空间用于 dp 数组,n 是输入数组的大小。
时间复杂度虽然更难计算,但与输入大小成线性关系。这是因为我们正在存储我们已经解决的子问题的答案,因此,我们有【O(n)个独特的子问题需要解决。这个结果也可以用我们使用自底向上方法得到的复杂度来验证。
-自下而上的方法
回想一下,在这种方法中,我们试图通过遵循迭代方法来消除所有的递归调用,其中我们从基本情况或“底部”开始,并向上进行。
让我们用访问 dp 的元素来替换对 f 的其他调用。
****dp[i] = max(arr[i] + dp[i + 2], dp[i + 1])****
基例呢,f(n-1)= arr【n-1】?这将是数组 dp 的最后一个元素。
****dp[n-1] = arr[n-1]****
就这样,我们有了自下而上 dp 方法的解决方案!
让我们实现它,就像我们对递归方法所做的那样。
这个函数可以这样调用:
****array := [1, 5, 2, 4, ...]
output(f(array))****
这里的复杂性在空间和时间上都是线性的。
为什么?
我们正在运行单个 for 循环 n-1 次,并且在每次迭代中,我们都在执行常数时间操作——线性时间复杂度。
由于数组 dp 的大小取决于输入数组的大小——当然,这是可变的——我们的空间复杂度也是线性的。
改进算法
但是我们能做得更好吗?让我们看看。
在渐近时间复杂度方面,我们做不到更好。为了找到答案,我们必须检查数组的每个元素。所以我们做不到比线性时间更好。
但是空间复杂度呢?我们需要维护一个大小为 n 的数组来解决问题吗?
仔细观察 for 循环中的代码行:
****dp[i] = max(arr[i] + dp[i + 2], dp[i + 1])****
在任何时间点,我们需要填充的DP【I】就是 dp 中的下两个元素——在索引 i +1 和 i + 2 。没有理由保持我们所有的结果。我们只需要跟踪最后两次迭代。
让我们在这里使用三个变量。为了便于联系,我们将它们命名为i0i1和I2。
****dp[i] --> i_0
dp[i+1] --> i_1
dp[i+2] --> i_2****
注意,在我们循环的下一次迭代中,我们的循环计数器 i ,变成了 i + 1 ,因为我们在每次迭代中都在递减 i 。DP【I+1】将是下一个DP【I+2】DP【I】将是下一个DP【I+1】和DP【I+2】——因为DP【I+1】我们就不需要它了****
用我们的三个新变量替换它,循环中的代码变成:
****i_0 := max(arr[i] + i_2, i_1)
i_2 := i_1
i_1 := i_0****
我们初始化这些变量就像我们的数组实现一样。
****dp[n-1] = arr[n-1] --> i_1 = arr[n-1]
dp[n] = 2 --> i_2 = 0****
需要记住的最后一件事是:如果输入数组只有一个元素会怎样?我们的循环,从 n-2 到 0 ,一次都不会运行。
因此,我们用值 i_1 初始化 i_0 。因此,如果循环从不运行—输入数组只有一个元素—返回 i_0 将返回 i_1 的值,这是数组中唯一的元素。
最后我们返回 i_0 而不是DP【0】。
****return dp[0] --> return i_0****
因此,我们最终的算法应该是这样的。
就像前面的动态编程方法一样,只需传入一个数组或对数组的引用就可以调用这个函数。
****array := [1, 5, 2, 4, ...]
return f(array)****
对于任意长度的数组,我们只需要三个变量。这样,我们算法的空间复杂度现在是 O(1) —常数。
总结我们的结果,
我们的实现摘要—作者使用 draw.io 创建的图像。
比较递归方法和我们的自顶向下方法,很明显我们是在用空间复杂度换取更好的时间复杂度。当然,因为两者都是递归的,所以它们有递归调用栈所需的额外空间。
同样,最低的两行是我们自底向上方法的结果。它们是迭代的,所以它们不需要在堆栈上递归存储函数记录。由于它们本质上与自顶向下方法的算法相同,所以它们具有相同的线性时间复杂度。
最好的情况是自底向上的方法,需要 O(1)个空间——这意味着我们的 dp 算法使用的空间不会随着输入大小 n 而改变。
代码
让我们用 C++实现我们的常数空间自底向上动态规划的最终算法。变量名和函数名与之前相同。
****注意:最后的空间复杂度优化步骤看起来稍微难一点,但是正如我们刚刚看到的,它极大地提高了您的空间利用率。看看你是否能在第 n 个斐波那契项的自下而上方法中找到类似的关系。
结论
动态编程通常不太直观或简单。话说回来,大多数复杂的事情都不是。但是通过练习,事情会变得更容易。网上有大量的动态编程练习题,这将帮助你更好地了解何时应用动态编程,以及如何更好地应用它。希望这篇文章是一个好的起点。
理解 ELECTRA 并训练一个 ELECTRA 语言模型
变形金刚模型如何学习语言?伊莱克特拉有什么新消息?如何在单个 GPU 上训练自己的语言模型?让我们来了解一下!
https://pix abay . com/photos/woman-studying-learning-books-1852907/#
内容
- 介绍
- 什么是前期培训?
- 蒙面语言模型(MLM)
- ELECTRA 预培训方法
- ELECTRA 方法的效率增益
- 训练你自己的 ELECTRA 模型
- 装置
- 数据准备
- 语言建模模型
- 训练模型
- 扔掉发生器,得到鉴别器
- 微调预训练模型
- 总结
介绍
训练一个用于特定自然语言处理任务的转换器模型的过程相当简单,尽管可能不太容易。从一个随机初始化的 Transformer 模型开始,放在一起一个巨大的(我指的是巨大的)数据集,其中包含您感兴趣的一种或多种语言的文本,在巨大的数据集上预先训练Transformer,并且使用您的特定任务数据集(可能相对较小),在您的特定任务上微调预先训练的 Transformer。
这种方法的优点是,最后一步(微调)只需要标记数据。就我个人而言,如果说我在深度学习方面的工作有什么不喜欢的地方,那就是给数据贴标签!我相信任何深度学习实践者都会同意,标记是耗时的、乏味的,并且可能容易出错。
什么是前期培训?
预训练是 Transformer model 学习 model 一种语言的过程。换句话说,转换器将学习表示文本序列的良好的、依赖于上下文的方式。这种知识可以在下游任务中重用,大大减少了所需的任务特定的、标记为的数据量,因为模型已经学习了语言特性,现在只需要微调其表示来执行特定的任务。对于预训练,在数据方面的唯一要求是大量(希望)干净的数据。不需要贴标签!我一想到如果有必要标记用于预先训练伯特的数十亿字节的文本,人们将不得不经历的折磨就不寒而栗。
这很好,但是转换器如何从大量未标记的文本中学习语言表示呢?嗯,最初的 BERT 模型依赖于两个预训练任务,【MLM】和 下一句预测 。在下一句预测中,该模型的任务是预测两个文本序列是否自然地相互跟随。据说这项任务有助于某些下游任务,如 BERT 论文中的问题回答和自然语言推理,尽管在后来的 RoBERTa 论文中显示这是不必要的,因为它只使用了掩蔽语言建模。不管怎样,我们更感兴趣的是第一种方法, MLM ,因为这正是 厄勒克特拉 预训练法旨在改进的地方。
蒙面语言模型(MLM)
在屏蔽语言建模中,一定比例的记号(记号是文本序列中一个单位的术语。它可以是一个单词或一个单词的一部分)被屏蔽,并且该模型的任务是为屏蔽的记号预测原始记号。被屏蔽的标记可以用实际的屏蔽标记(例如[MASK])替换,或者用来自词汇表的另一个随机标记(模型已知的所有标记的集合)替换。
配备了这种预训练技术的 BERT 能够在许多下游 NLP 任务中粉碎先前设置的基准。然而, ELECTRA 论文的作者注意到 MLM 方法只从任何给定例子的屏蔽记号(通常为 15%)中学习。这导致用 MLM 训练语言模型所需的计算资源大幅增加。 MLM 的另一个缺点是掩码令牌只出现在预训练阶段,而不会在微调或下游使用期间出现。这种差异也导致使用 MLM 训练的模型性能略有下降。
ELECTRA 预培训方法
ELECTRA ( E 有效地 L 获得En 以 C 分类 T oken R 替换 A 精确地)是一种新的预训练方法,旨在匹配或超过 MLM 预训练模型的下游性能,同时在预训练阶段使用明显更少的计算资源。ELECTRA 中的预训练任务基于检测输入序列中被替换的标记。这个设置需要两个变压器模型,一个发生器和一个鉴别器。
ELECTRA 预培训设置(来源— ELECTRA 论文)
让我们一步一步地分解训练前的流程。
- 对于给定的输入序列,用一个【掩码】记号随机替换一些记号。
- 发生器预测所有屏蔽记号的原始记号。
- 鉴别器的输入序列是通过用发生器预测替换【屏蔽】标记构建的。
- 对于序列中的每个令牌,鉴别器预测它是原始的还是已经被生成器替换。
生成器模型被训练来预测屏蔽记号的原始记号,而鉴别器模型被训练来预测在给定损坏序列的情况下哪些记号已经被替换。这意味着鉴别器损耗可以在所有输入令牌上计算,因为它对每个令牌执行预测。使用 MLM ,仅在屏蔽令牌上计算模型损失。这是两种方法之间的关键区别,也是 ELECTRA 效率更高的主要原因。
这种设置类似于 GAN(生成对抗网络)的训练设置,除了生成器没有被训练来试图欺骗鉴别器(因此它本身不是对抗的)。此外,如果生成器碰巧正确预测了屏蔽令牌的原始令牌,则该令牌被认为是原始令牌(因为该令牌没有被破坏/改变)。
**鉴别器模型用于下游任务,而发生器在预训练后被丢弃。
ELECTRA 方法的效率增益
ELECTRA 论文强调了 ELECTRA 预培训方法的主要改进,并阐明了为什么会看到这些改进。
除非另有说明,所有分数都是 ELECTRA 论文中给出的胶水基准分数。
针对所有输入令牌和仅屏蔽令牌定义的损失
如前所述,鉴别器模型的损失是在序列中的所有记号上定义的,因为它必须预测每个记号是原始的还是替换的。为了证明这种差异的重要性,作者将 ELECTRA 模型与经过相同训练的模型 (ELECTRA 15%) 进行了比较,除了 ELECTRA 15% 只计算了屏蔽令牌的鉴频器损耗。胶水基准用于比较。
可以看出,原始的 ELECTRA 方法得到了 85.0 的分数,而 ELECTRA 15% 得到了 82.4 。(作为对比,伯特得了 82.2 分)
ELECTRA 还与另一个模型(全令牌 MLM) 进行比较,其中屏蔽令牌被替换为生成器预测,该模型的任务是预测输入中所有令牌的原始身份。损失也是在所有输入令牌上计算的。全代币 MLM 车型得分 84.3 ,超过伯特的82.2,直追伊莱克特拉的 85.0 。
这清楚地表明,计算所有输入令牌的损失的能力显著地提高了预训练模型的性能。
预训练和微调之间的屏蔽令牌差异
为此,将原始的伯特模型与使用 MLM 预训练目标训练的(替换 MLM)* 模型进行比较,除了屏蔽令牌被替换为来自生成器的令牌而不是实际的【屏蔽】令牌。*
取代 MLM 车型得分 82.4 ,略胜伯特的82.2。这种差异表明,预训练/微调差异对伯特的表现略有损害。
相比之下,从所有输入标记中学习似乎比解决屏蔽标记的预训练/微调不匹配具有更大的影响。
在表格中总结比较结果;
模型比较(来源— ELECTRA 论文)
适用于较小型号的 ELECTRA 与 BERT
与 BERT 相比, ELECTRA 的性能增益在较小的模型尺寸下更大。
ELECTRA 和 BERT 之间的模型尺寸比较(来源— ELECTRA 论文)
训练你自己的 ELECTRA 模型
ELECTRA 预训练方法的一个巨大优势是可以在单个 GPU 上训练你自己的语言模型!
下面,我将向您展示如何使用简单变形金刚库来训练您自己的语言模型。
装置
- 从这里安装 Anaconda 或 Miniconda 包管理器。
- *创建新的虚拟环境并安装软件包。
conda create -n simpletransformers python pandas tqdm
conda activate simpletransformers
* - 如果您使用 fp16 培训,请安装 Apex。请遵循此处的说明。(从 pip 安装 Apex 给一些人带来了问题。)
- 安装简单变压器。
pip install simpletransformers
数据准备
我们将在世界语上训练我们的语言模型(灵感来自拥抱脸的教程这里)。不会世界语也不用担心,我也不会!
为了预先训练一个模型,我们需要一个世界语的(最好是大的)文本语料库。我将使用莱比锡全集中的世界语文本文件。具体来说,我下载了以下数据集:
- 2011 年—混合(100 万句)
- 2012 年—新闻抓取(100 万句)
- 2012 年—网络(100 万句)
- 2016 —维基百科(30 万句)
通过使用更大的数据集,你应该能够改善结果。
下载数据集并将档案解压到一个目录data/
。
将所有“句子”文件移动到data/
目录中。
面向 Linux/bash 用户的脚本。(别人:你怎么不上 Linux?😉)
*for d in */;
do mv d/*sentences.txt .;
done;*
如果您打开其中一个文件,您会注意到它们有两列,第一列包含索引,第二列包含文本。我们只需要文本,所以我们将删除索引,合并所有文本,并将文本分成训练和测试文件。
现在我们准备开始训练了!
语言建模模型
在简单的转换器中,所有的语言建模任务都用LanguageModelingModel
类来处理。在简单的转换器中执行任何 NLP 任务时,您都可以使用大量的配置选项,尽管您不需要设置每个选项(尽可能使用合理的默认值)。
常见配置选项及其用法列表 此处 。
语言造型具体选项及其用法 此处 。
以上要点建立了一个可以用来训练我们新模型的LanguageModelingModel
。
在泰坦 RTX GPU 上,单个训练周期(采用这种配置)需要不到 2 个小时。为了加快训练速度,你可以增加evaluate_during_training_steps
或者干脆关掉evaluate_during_training
。
当用简单的转换器从零开始训练一个语言模型时,它会自动从指定的train_file
为我们创建一个标记器。您可以通过在train_args
中设置一个vocab_size
来配置经过训练的分词器的大小。在我的例子中,我使用了 52000 个词汇。
您也可以根据需要配置发生器和鉴别器模型的架构。配置选项设置在train_args
中的两个字典generator_config
和discriminator_config
中。ELECTRA 论文推荐使用尺寸为鉴别器的 0.25-0.5 倍的发生器型号。他们还建议减少隐藏层的数量,并保持发生器和鉴别器之间的其他参数不变。考虑到这一点,我为鉴别器和一个类似的生成器选择了一个小(12 层)架构,尽管隐藏层数只有原来的 1/4。
*"generator_config": {"embedding_size": 128,"hidden_size": 256,"num_hidden_layers": 3,},"discriminator_config": {"embedding_size": 128,"hidden_size": 256,}*
你可以在这里 找到所有的架构配置选项及其默认值 。
训练模型
既然我们已经建立了模型,我们需要做的就是开始训练。
运行上面的脚本将开始训练我们的语言模型!
语言模型的训练损失
可以看看所有的训练进度信息(还有好多图表!)这里在 Wandb 上。
令人惊讶的是,ELECTRA 预训练方法让我们可以在几个小时内,在单个 GPU 上训练一个全新的语言模型!下面的图表讲述了完整的故事。
培训损失与时间
曲线的平坦部分是简单变压器进行评估的地方。遗憾的是,我忘了为评估设置一个更大的批量,所以评估比预期慢了很多。
扔掉发生器,得到鉴别器
当使用 ELECTRA 预训练方法时,通常在训练后丢弃发生器,仅使用鉴别器。为此,一旦训练完成,简单的变压器将分别保存发生器和鉴别器。
但是,如果您在训练完成前终止训练,您的模型将被保存为一个包含鉴别器和生成器的LanguageModelingModel
。为了分别提取鉴别器或发生器,您可以使用save_discriminator()
或save_generator()
方法。
下面的脚本演示了如何加载一个LanguageModelingModel
并分别提取生成器模型。
这将把在outputs/best_model
中找到的经过训练的语言模型加载到LanguageModelingModel
中,然后只把鉴别器保存到discriminator_trained/discriminator_model
中。
微调预训练模型
现在我们有了一个可以“理解”世界语的模型,我们可以继续在一个特定的 NLP 任务中对它进行微调。为此,我们可以使用拥抱脸提供的世界语中的词性标注数据集(训练和标签)。
在我的例子中,我下载了文件并保存到data/pos-tagging
。
利用这一点,我们可以在词性数据集上将我们的 ELECTRA 预训练模型(即鉴别器)训练为NERModel
。(关于变形金刚的命名实体识别的更多信息,你可以在这里查看我的文章
结果如下:
*eval_loss = 0.18994220502207695f1_score = 0.9025234264922376precision = 0.9197880191456026recall = 0.8858950119055756*
如您所见,该模型只需几个小时的训练就能在下游任务中表现出色!
总结
伙计们,这就是如何用 ELECTRA 从头开始训练一个全新的语言模型!**
我对这种可能性感到兴奋,因为 ELECTRA 应该大大降低训练自己的语言模型的计算资源障碍。希望我们将会看到越来越多的语言模型被开发出来用于世界上所有的语言!
一如既往,一个巨大的大喊拥抱脸和他们的变形金刚库,谁的工作使这一切成为可能!
理解熵:机器学习的黄金标准
来源: Unsplash
从决策树到神经网络
TL;DR: 熵是对系统中混沌的一种度量。因为它比其他更严格的指标(如准确性或均方误差)更具动态性,所以使用熵来优化从决策树到深度神经网络的算法已经显示出可以提高速度和性能。
它在机器学习中无处不在:从决策树的构建到深度神经网络的训练,熵都是机器学习中必不可少的度量。
熵源于物理学——它是对系统中无序或不可预测性的一种度量。例如,考虑一个盒子里的两种气体:最初,系统具有低熵,因为这两种气体是完全可分的;然而,一段时间后,气体混合,系统的熵增加。据说,在一个孤立的系统中,熵永远不会减少——没有外力,混沌永远不会变暗。
例如,考虑掷硬币——如果掷硬币四次,事件出现[tails, heads, heads, tails]
。如果你(或一个机器学习算法)要预测下一次掷硬币,你将能够以任何确定性预测结果——该系统包含高熵。另一方面,带有事件[tails, tails, tails, tails]
的加权硬币具有非常低的熵,根据当前的信息,我们几乎可以肯定地说下一个结果将是反面。
大多数适用于数据科学的场景都介于极高熵和极低熵之间。高熵意味着低信息增益,低熵意味着高信息增益。信息增益可以被认为是系统中的纯度:系统中可用的干净知识的数量。
决策树在其构造中使用熵:为了尽可能有效地将输入沿着一系列条件导向正确的结果,具有较低熵(较高信息增益)的特征分裂(条件)被放置在树上较高的位置。
为了说明低熵和高熵条件的概念,假设要素的类别由颜色(红色或蓝色)标记,分割由垂直虚线标记。
决策树计算特征的熵,并对它们进行排列,使得模型的总熵最小(信息增益最大)。从数学上来说,这意味着将最低熵条件放在顶部,这样它可以帮助它下面的分裂节点减少熵。
决策树训练中使用的信息增益和相对熵被定义为两个概率质量分布 p ( x )和 q ( x )之间的“距离”。它也被称为 kull back-lei bler(KL)散度或运土机距离,用于训练生成的对抗性网络,以评估生成的图像与原始数据集中的图像相比的性能。
神经网络最喜欢的损失函数之一是交叉熵。无论是分类的、稀疏的还是二元交叉熵,该度量都是高性能神经网络的默认定位损失函数之一。它还可以用于优化几乎任何分类算法,如逻辑回归。像熵的其他应用一样,如联合熵和条件熵,交叉熵是熵的许多严格定义中的一种,适合于一个独特的应用。
交叉熵,像库尔巴克-利伯散度(KLD),也处理两个分布 p 和 q 之间的关系,代表真实分布 p 和近似分布 q 。然而,KLD 度量的是两个分布之间的相对熵,而交叉熵度量的是两个分布之间的“总熵”。
该度量被定义为使用模型分布 q 对来自具有分布 p 的源的数据进行编码所需的平均比特数。如果我们考虑一个目标分布 p 和近似值 q ,我们会想要减少使用 q 而不是 p 来表示一个事件所需的比特数。另一方面,相对熵(KLD)测量在分布 q 中表示来自 p 的事件所需的额外比特数。
交叉熵可能看起来像是一种度量模型性能的迂回方法,但它有几个优点:
- 基于准确性/误差的度量有几个问题,包括对训练数据的顺序极度敏感,不考虑置信度,并且对可能给出错误结果的数据的各种属性缺乏鲁棒性。它们是非常粗糙的绩效衡量标准(至少在训练期间)。
- 交叉熵度量信息内容,因此比仅仅强调勾选所有方框的指标更加动态和健壮。预测和目标被视为分布,而不是一系列等待回答的问题。
- 它与概率的性质密切相关,尤其适用于 sigmoid 和 SoftMax 激活(即使它们仅用于最后一个神经元),有助于减少消失梯度问题。逻辑回归可以被认为是二元交叉熵的一个版本。
虽然熵并不总是最好的损失函数——特别是在目标函数 p 没有明确定义的情况下——但它通常会提高性能,这解释了它为什么到处存在。
通过在机器学习中使用熵,它的核心组成部分——不确定性和概率——可以通过交叉熵、相对熵和信息增益等概念得到最好的表达。熵在处理未知方面是显而易见的,这是建模中非常需要的。当模型基于熵进行优化时,它们能够带着更高的知识和目的感在不可预测的平原上漫游。
感谢阅读!
如果你对最新的文章感兴趣,可以考虑订阅。如果你想支持我的写作,通过我的推荐链接加入 Medium 是一个很好的方式。干杯!
可解释的人工智能:激活最大化,灵敏度分析,等等
towardsdatascience.com](/every-ml-engineer-needs-to-know-neural-network-interpretability-afea2ac0824e) [## 每个机器学习算法都可以表示为一个神经网络
算法中的算法
towardsdatascience.com](/every-machine-learning-algorithm-can-be-represented-as-a-neural-network-82dcdfb627e3)
除非另有说明,所有图表均由作者创建。
了解实验指标
定义基线测量和转换率
在我的实验设计系列的第一部分思考实验设计,我们讨论了实验的基础:目标、条件和度量。在本帖中,我们将离开最初的实验设置,开始理解基线指标以及选择适当的转换指标和比率的细微差别。
简介
我介绍了一个商业环境中的实验的目标,使用柠檬水摊位的例子:在可比较的时间范围内,在受控条件下(杯子的颜色),测量结果(售出杯子的数量)的差异。在这个例子中,我没有提到任何实验的一个关键要素——一个假设,一个基于有限证据的初步推测。严格地说,实验的目的是验证或反驳一个假设提出的观点。对于我们的柠檬水摊位示例,一个合理的假设可能是“柠檬水杯子的颜色影响售出的杯子数量。”
从基线开始
如果我们刚刚开始我们的柠檬水摊位(或其他小企业/业务线),你能看到立即设计这个特定的实验(或任何实验)来验证一个假设的问题吗?撇开市场调查不谈,我们没有任何可靠的(由硬数据支持的)预测来预测我们售出的杯子数量!
如果我们红色杯子的前两周销量很差,接下来的两周蓝色杯子的销量有所增加,我不会轻易得出蓝色杯子更好的结论。销售额的突然变化可能是因为人们在开业前几周不知道我们的摊位,直到最近才发现我们的摊位。记住,实验的目标是了解增量变化的效果。当我们引入一个大的变化(即开始我们的摊位或彻底改造我们的店面),我们将不稳定性引入我们现有的业务,这将掩盖和/或扭曲我们的实验条件的影响。
在进行实验之前,重要的是建立一个稳定的基线,用于判断我们的增量变化的效果。
为了让我们的例子更具体,假设我们已经经营了一年半的柠檬水摊位。我们在社区中相对知名,但是我们已经没有办法继续发展我们的业务了。在对我们的业务目标进行优先排序并对相关转化指标和比率*进行头脑风暴后,我们决定分析转化指标:
售出的杯子数量/光顾我们摊位的人数。
我生成了一些虚拟数据,代表我们去年的月销售额。我们的业务相对于转换率的稳定性。现在,当我们引入我们的实验条件(改变杯子的颜色)时,我们有了一个可靠的可预测的基线转化率(平均 3.62%),我们可以用它来比较我们的新结果。
作者图片
思考我们转化率的变化
在这一点上,很容易忘记我们的目标指标是转换率,并开始头脑风暴的增量变化,增加售出的杯子数量。使用转换率而非绝对值要求我们将关注点从单一指标扩展到两个相关指标之间的关系,从关注规模扩展到关注规模效率&。
为了提高我们的转换率,我们必须制定一个策略,使售出杯子的数量增长速度快于光顾我们摊位的人数增长速度。改变杯子的颜色或设计可能是一个有趣的商业计划;这是假设我们相信人们驻足我们的摊位主要是为了柠檬水和我们漂亮的摊位,而不是因为杯子的颜色。
从商业角度来看,我们经常读到使用数据和实验来提供可操作的见解。除了我们的基线速率之外,在我们的每个实验条件下设定合理的目标速率也很重要;我们不想对转换率的任何微小变化采取行动。设定目标转换率既是一门科学,也是一门艺术,可以基于过去的数据和直观的商业感觉。在我们的柠檬水的例子中,我们可以说,如果我们的转换率在接下来的几个月里是 4.12%,增加了 0.5%,我们将改变杯子的颜色。
结论:
总结一下我们在柠檬水示例中取得的成果:
- 我们定义了我们的商业目标:增加售出的杯子
2.我们定义了我们的转换指标和转换率:售出的杯子&售出的杯子/客流量
3.我们开发了一个可控的增量变化,它将(假设地)影响我们的结果
4.我们建立了一个比较稳定的基线。
如果我们的转换指标达到了目标,我们就可以在新的条件下开展业务,对吗?不完全是。
到目前为止,你可能已经意识到,我没有在我们的实验中引入任何统计数据!在这一点上,可能还不完全清楚为什么我们需要统计数据来验证我们的假设和完成我们的实验。尽管如此,在处理这种不确定性时,我们需要一种量化决策过程的方法;我们用统计学来量化实验证据的强度。在本系列的下一篇文章中,我将开始介绍基本统计概念和检验统计的思想,因为它们适用于我们的实验设计。
[1]:莫妮卡·瓦希。(2020).实验设计的数据科学。领英学习。https://www . LinkedIn . com/learning/the-data-science-of-experimental-design
理解 Power BI 中的显式和隐式度量
检查 Power BI 数据模型中隐式和显式度量之间的重要差异,以及为什么您应该考虑避免更简单的方法
Alex Andrews 在 Pexels.com 拍摄的照片
对于新的 Power BI 用户来说,最具挑战性的概念之一是理解度量和计算列之间的差异。或者说得更具体一点,概念本身并不是什么大问题,但 Power BI 菜鸟最让人望而生畏的,就是要明白什么时候用哪个。
因为我已经写了计算列和度量值的用例,并且简要解释了当您想要使用其中每一个时的不同场景,所以在最近的数据之星会议上,我有一个问题要解释显式和隐式度量值之间的区别。我已经在这里简短地回答了,但是我答应为这个话题写一篇单独的文章。
因此,在本文中,我将只关注度量,并尝试深入解释显式和隐式度量之间的差异。
隐性的,显性的……什么?!
好了,你已经听说过功率 BI 中的度量,就是这样。现在究竟什么是隐性措施?!还是露骨?不要惊慌,继续阅读,我向你保证,在这篇文章结束时,你会对这两者有很好的理解,以及它们的主要优点和缺点是什么。
隐性措施—【感谢】动力匕
首先,我知道我们都喜欢自动为我们创造的东西,Power BI 在这方面做得很好。Power BI 为我们做的事情之一是创建隐式度量。
正如您在上面的插图中所注意到的,Power BI 识别了我们的数据模型中的数值字段,并自动用适马符号对它们进行了标记。这意味着,一旦将这些列值拖到报表视图中,就会对它们进行汇总。
让我们看看这在现实中是如何工作的:
我将我的 productID 列拖到表 visual 中,我看到 Power BI 自动应用了某种聚合。现在,您可以定义要应用于特定列的聚合类型,或者根本不进行聚合(提示:选择不汇总选项):
这里,Power BI 对我的 productID 列执行了 Count aggregate 函数。看着上面的图片,人们可以(太)容易地得出结论,当使用隐式度量时有很大的灵活性-您可以在一组预定义的聚合之间进行选择,甚至包括花哨的统计计算,如标准差、方差或中位数…所有这些,只需单击一下!
那么,既然(几乎)一切都已经为我们预烤好了,为什么还要有人费心去写 DAX 呢?
在我向您展示为什么使用隐式度量会回来困扰您之前,让我简单地概述一下隐式度量如何在您的数据模型中与非数值字段一起工作。
我的文本字段 InteractionType 可以总结为四种不同的方式:First、Last、Count (Distinct)和 Count。当然,它也可以是非汇总的,就像上面的例子一样。
类似地,日期列也提供了自己的一组预定义聚合:
隐性措施的局限性
无论通过使用自动创建的度量来节省时间和精力的可能性看起来多么有吸引力,您都应该尽量避免,因为它有一些明显的缺点。
假设您有一个非加性或半加性的度量,比如产品的单价,或者银行账户余额。您不希望在报告中简单地对这些值求和,因为这不是这些度量的预期行为。因此,如果使用隐式度量,很容易发生您的报告产生意想不到的错误结果。
隐式度量的另一个限制是不能在多个不同的聚合类型中使用它们。假设我既想知道我的客户总数,又想知道独特客户的总数。通过使用隐式度量,我只能选择其中一个选项…
明确的措施=更大的灵活性!
使用 DAX 语言以显式方式编写度量,在开始时需要更多的时间和精力,因为你需要做一些手工工作。但是,你以后会结出果实的,相信我。
回到我们之前的挑战—在我们的报告中显示客户总数和唯一(独特)客户总数可以使用显式方法轻松解决:
Total Customers = COUNT(TableName[CustomerID])
Total Unique Customers = DISTINCTCOUNT(TableName[customerID])
因此,正如您所注意到的,我们使用同一个列作为多个不同度量的引用,以产生期望的结果。
虽然隐式度量可以支持一些非常基本的场景,但是一旦您的报表需要更复杂的计算,您就必须切换到显式度量。
然而,使用显式度量而不是隐式度量的主要优点是它们的 可重用性 。您只需定义一次度量,就可以根据需要多次引用它。
另一个明显的好处是更容易维护数据模型。如果创建基本显式度量,例如:
Sales Amt = SUM(TableName[Sales Amount])
您可以在 20 个不同的衡量标准中使用此衡量标准作为参考(例如,计算毛利润、年同比等。)!如果需要更改任何后台逻辑,只需在一个地方(在基本度量中)进行更改,所有引用的度量将自动应用新的逻辑。
措施—最佳实践
现在,您已经了解了隐式和显式度量之间的区别以及使用后者的明显优势,让我总结一些关于在您的报告中使用度量的最佳实践:
Pexels.com 上永恒幸福的照片
- 不要忘记正确设置度量值的格式 —这意味着,如果您正在处理与金钱相关的值(例如销售额),您可能希望将它们设置为货币格式。与格式保持一致,如果你的数字限制在两位小数,那么在整个报告中坚持使用它
- 一旦您完成了基于特定列的显式度量的创建,您应该在报表 中隐藏该列。这样,您就可以防止对该列的不充分使用(例如,对账户的银行余额进行简单求和)。因此,作为数据建模者,您负责汇总选项
在本例中,我定义了“客户总数”和“唯一客户总数”度量值,所以我不希望有人执行 customerID 的求和。因此,我将隐藏字段列表中的 customerID 列。
- 将您的度量组织到单独的文件夹中 —当您的报表只有几个度量时,这不是问题。但是,当您需要操作数十甚至数百个度量时,事情会变得更加复杂。为了防止这种情况,我总是使用下面的技巧来更好地组织我的度量。默认情况下,该度量将位于您创建它的表中。您可以通过单击该度量来更改它,然后在建模选项卡下,更改该特定度量的主表:
然而,这只是将度量从一个表移动到另一个表,并不能解决问题。为了解决这个问题,我需要创建一个全新的表,只保存我的度量。
在 Home 选项卡下,选择 Enter Data 并创建一个名为 RepMeasures:
单击 Load,您将在模型中看到一个新表。之后,单击您的度量,并在 Home 表下选择 RepMeasures。
最后,只需简单地删除第 1 列,就可以了。
这样,您可以对您的度量进行分离和分组。相信我,这将使您的生活变得更加容易,尤其是当您在报表中创建多个度量时。
结论
正如我已经说过的:我们都喜欢走一条更容易的路来实现我们的目标。这是完全合理的,Power BI 是您在这条道路上的“最好的朋友”。
但是,在选择采用哪种方法时,有许多重要的考虑因素需要考虑。我不想说:千万不要用隐性措施!通过写这篇文章,我只是想指出在使用它们时一些可能的陷阱和限制,以及为什么您仍然应该更喜欢写显式的方法。
感谢阅读!
成为会员,阅读 Medium 上的每一个故事!
订阅这里获取更多有见地的数据文章!
用 Viola-Jones 对象检测框架理解人脸检测
了解最流行的人脸检测框架之一
在我的毕业照中检测到面孔
一.动机
从 Instagram 滤镜到自动驾驶汽车,计算机视觉技术现在已经深深融入了许多人的生活方式。一个重要的计算机视觉应用是让计算机检测图像中的物体的能力。在这些对象中,人脸最受关注,因为它在安全和娱乐方面有许多有用的应用。因此,本文主要关注一个流行的人脸检测框架,称为 Viola-Jones 对象检测框架。
这里的目的是向您提供对框架的理解,以便您可以放心地使用 OpenCV 提供的开源实现。我想帮助您理解幕后发生的事情,并希望让您更加欣赏 OpenCV 这样的库为您处理所有的复杂性。如果你想要一个“从头开始”的实现,可以参考 Anmol Parande 的这个 Python 实现。我也推荐你看看 Ramsri 关于这个话题的视频。
二。概念
由 Paul Viola 和 Michael Jones 早在 2001 年开发的 Viola-Jones 对象检测框架可以快速准确地检测图像中的对象,并且特别适合人脸(Viola & Jones,2001)。尽管年代久远,该框架仍然是人脸检测领域的领军人物,与其 CNN 的许多对应部分不相上下。Viola-Jones 对象检测框架结合了类 Haar 特征、积分图像、AdaBoost 算法和级联分类器的概念,创建了一个快速准确的对象检测系统。因此,为了理解框架,我们首先需要单独理解这些概念,然后弄清楚它们是如何连接在一起形成框架的。
如果您已经了解下面的概念,并且只是想知道这些概念是如何协同工作的,您可以跳到第三部分。
类哈尔特征
图 1:类哈尔特征(上)和如何计算它们(下)。
通常在计算机视觉中,特征是从输入图像中提取的,而不是直接使用它们的强度(RGB 值等)。类似哈尔的特征就是一个例子。其他例子包括方向梯度直方图(HOG)(LBP)等。类哈尔特征由暗区域和亮区域组成。它通过取亮区域的强度之和并减去暗区域的强度之和来产生单个值。有许多不同类型的 Haar-like 特征,但是 Viola-Jones 对象检测框架只使用图 1 中的那些。不同类型的 Haar-like 特征让我们从图像中提取有用的信息,例如边缘、直线和对角线,我们可以用它们来识别对象(即人脸)。
整体图像
图 2:原始图像到积分图像的转换(上)以及如何使用积分图像计算矩形区域(下)
积分图像是图像的中间表示,其中积分图像上的位置值( x ,y)等于原始图像上( x 【T7, y )位置上方和左侧(包括两端)的像素之和(Viola & Jones,2001)。这种中间表示是必不可少的,因为它允许快速计算矩形区域。为了说明,图 3 示出了红色区域 D 的总和可以在恒定时间内计算,而不是必须遍历该区域中的所有像素。由于提取 Haar-like 特征的过程包括计算暗/亮矩形区域的总和,积分图像的引入大大减少了完成这项任务所需的时间。
AdaBoost 算法
图 3:使用 AdaBoost 算法的目的是从 n 个特征中提取最佳特征。注意:最佳特征也称为弱分类器。
AdaBoost(自适应增强)算法是一种机器学习算法,用于在所有可用特征中选择最佳特征子集。该算法的输出是一个分类器(也称为预测函数、假设函数),称为“强分类器”。强分类器由【弱分类器】(最佳特征)的线性组合组成。从高层次来看,为了找到这些弱分类器,该算法运行 T 次迭代,其中 T 是要找到的弱分类器的数量,由您设置。在每次迭代中,该算法会找出所有特征的错误率,然后选择该次迭代中错误率最低的特征。
你可以参考 StatQuest 的这个有趣的视频来更详细地了解 AdaBoost 算法
级联分类器
图 4:级联分类器
级联分类器是一种多级分类器,可以快速准确地进行检测。每个阶段由 AdaBoost 算法产生的强分类器组成。从一个阶段到另一个阶段,强分类器中的弱分类器的数量增加。输入是按顺序(逐级)评估的。如果用于特定阶段的分类器输出否定结果,则输入被立即丢弃。如果输出为正,则输入被转发到下一级。根据 Viola & Jones (2001),这种多阶段方法允许构建更简单的分类器,然后可以用于快速拒绝大多数负面(非面部)输入,同时在正面(面部)输入上花费更多时间。
三。基于 Viola-Jones 目标检测框架的人脸检测
在学习了 Viola-Jones 对象检测框架中使用的主要概念后,您现在可以学习这些概念是如何协同工作的了。该框架包括两个阶段:培训和测试/应用。让我们一个一个来看。
培养
这一阶段的目标是为一个人脸生成一个级联分类器,它能够准确地对人脸进行分类,并快速丢弃非人脸。为此,您必须首先准备好训练数据,然后通过对该训练数据使用改进的 AdaBoost 算法来构建级联分类器。
- 数据准备
概念:积分图像+类哈尔特征
图 5:数据准备过程
假设你已经有了一个由正样本(人脸)和负样本(非人脸)组成的训练集,第一步就是从那些样本图像中提取特征。Viola & Jones (2001)推荐图像为 24 x 24。由于每种类型的 Haar-like 特征在 24×24 窗口中可以具有不同的大小和位置,因此可以提取超过 160,000 个 Haar-like 特征。尽管如此,在这个阶段,需要计算所有 160,000+ Haar-like 特征。幸运的是,积分图像的引入有助于加速这一过程。图 5 展示了数据准备的整个过程。
2。用改进的 AdaBoost 算法构建级联分类器
图 6:构建级联分类器的过程
概念:AdaBoost 算法+级联分类器。
可以想象,直接使用全部 160,000+个特征,计算效率很低。Viola & Jones (2001)提出了两种解决方案。首先,使用 AdaBoost 算法将特征数量减少到只有少数有用的特征。其次,将剩余的特性分成几个阶段,并以逐阶段(级联)的方式评估每个输入。Viola & Jones (2001)设计了 AdaBoost 算法的修改版本,以便能够训练级联分类器。图 6 显示了 Ramsri 在其视频(Ramsri,2012)中提供的算法的简化版本。
在他们的论文中,Viola & Jones (2001)提到他们的级联分类器有 38 个阶段(38 个强分类器),由超过 6000 个特征组成。
测试/应用
图 7:Viola-Jones 对象检测框架中的滑动窗口检测过程
想象一下,我们需要检测上图中的人脸。Viola & Jones (2001)使用滑动窗口方法,其中不同比例的窗口在整个图像上滑动。比例因子和移动步长是供您决定的参数。所以对于上图,有 m 子窗口来评估。对于子窗口 i ,框架将该子窗口的图像大小调整为 24 x 24 的基本大小(以匹配训练数据),将其转换为完整的图像,并通过在训练阶段产生的级联分类器进行馈送。如果子窗口通过级联分类器中的所有阶段,则检测到人脸。
四。结论
总之,您已经了解了 Viola-Jones 对象检测框架及其在人脸检测中的应用。今天的许多技术都受益于保罗·维奥拉和迈克尔·琼斯的工作。通过理解框架如何工作,你可以自信地实现你自己的工作版本,或者使用 OpenCV 提供的开源实现。我希望我的解释能让你朝着那个方向前进,并促使你使用这个令人敬畏的框架创造出令人惊叹的技术。
喜欢这篇文章并想表达您的支持?关注我或者给我买咖啡
参考
岑,K. (2016)。Viola-Jones 实时人脸检测器的研究。检索自https://web . Stanford . edu/class/cs 231 a/prev _ projects _ 2016/cs 231 a _ final _ report . pdf
h .詹森(2008 年)。实现 Viola-Jones 人脸检测算法。DTU 丹麦技术大学博士论文。检索自https://pdfs . semantic scholar . org/40 B1/0e 330 a 5511 a 6a 45 f 42 c8 b 86 da 222504 c 717 f . pdf
Mordvintsev 和 k . Abid(2013 年)。基于哈尔级联的人脸检测。 OpenCV-Python 教程。检索自https://opencv-python-tutro als . readthedocs . io/en/latest/py _ tutorials/py _ obj detect/py _ face _ detection/py _ face _ detection . html
帕帕乔治欧,C. P .,柳文欢,m .,&波焦,T. (1998)。一个通用的目标检测框架。第六届国际计算机视觉会议。555–562。doi:101109/iccv . 1998.710772
拉姆斯瑞。(2012 年 9 月 16 日)。 Viola Jones 人脸检测与跟踪讲解。从 https://youtu.be/WfdYYNamHZ8取回
维奥拉,p .和琼斯,M. (2001)。使用简单特征的增强级联的快速对象检测。2001 年 IEEE 计算机学会计算机视觉和模式识别会议论文集,1 ,I-511 — I-518。doi: 10.1109/CVPR
理解变脸者:一个新的变脸模型
简单而完整的解释
如今,深度学习可以在图像合成和处理领域产生惊人的结果。我们已经看到了让虚构人物产生幻觉的网站、展示名人说出他们从未说过的话的视频和让人们跳舞的工具,这些都有足够的真实性来愚弄我们大多数人。其中一个新颖的壮举是 FaceShifter [1],这是一个深度学习模型,可以交换图像中的人脸,性能超过最先进的技术。在本文中,我们将了解它是如何工作的。
问题陈述
我们有一个源人脸图像 Xₛ和一个目标人脸图像 Xₜ,我们希望生成一个新的人脸图像 Yₛₜ,它具有 Xₜ的属性(姿势、灯光、眼镜等),但具有 Xₛ.人的身份图 1 总结了这一问题陈述。现在,我们继续解释这个模型。
图一。换脸的问题陈述。显示的结果来自 FaceShifter 模型。改编自[1]。
变脸者模型
FaceShifter 由两个网络组成,分别叫做 AEI-Net 和 HEAR-Net。AEI-Net 产生一个初步的面部交换结果,而 HEAR-Net 对这个结果进行提炼。让我们打破这个管道。
AEI-Net
AEI-Net 是“自适应嵌入式集成网络”的缩写。这是因为 AEI-Net 由 3 个子网络组成:
- 身份编码器:将 Xₛ嵌入到描述图像中人脸身份的空间中的编码器。
- 多级属性编码器:一种编码器,用于将 Xₜ嵌入到一个空间中,该空间描述了我们交换人脸时想要保留的属性。
- AAD 生成器:一个生成器,它整合了前面两个子网络的输出,生成了与 Xₛ.身份交换的 Xₜ的脸
AEI-Net 如图 2 所示。让我们充实它的细节。
图二。AEI-Net 的体系结构。改编自[1]。
身份编码器
这个子网络将源图像 Xₛ投影到低维特征空间。输出只是一个向量,我们称之为 zᵢ,如图 3 所示。这个向量编码了 Xₛ人脸的身份,这意味着它应该提取我们人类用来区分不同人的人脸的特征,如他们眼睛的形状,眼睛和嘴巴之间的距离,嘴巴的曲率等等。
作者使用预先训练好的编码器。他们使用了一个经过人脸识别训练的网络。这有望满足我们的要求,因为区分人脸的网络必须提取与身份相关的特征。
图 3。身份网络。改编自[1]。
多级属性编码器
这个子网对目标图像 Xₜ.进行编码它产生多个矢量,每个矢量以不同的空间分辨率描述 Xₜ的属性,特别是 8 个矢量,称为 zₐ.这里的属性指的是目标图像中人脸的配置,如人脸的姿势、轮廓、面部表情、发型、肤色、背景、场景照明等。如图 4 所示,它是一个具有 U 形网络结构的 ConvNet,输出向量只是上扩/解码部分中每一级的特征映射。请注意,这个子网不是预先训练的。
图 4。多级属性编码器体系结构。改编自[1]。
将 Xₜ表示为多个嵌入是必要的,因为在单个空间分辨率下使用单个嵌入将导致产生具有交换面的所需输出图像所需的信息丢失(即,有太多我们想要从 Xₜ保留的精细细节使得压缩图像不可行)。这在作者进行的消融研究中很明显,他们试图仅使用前 3 个 zₐ嵌入而不是 8 个来表示 Xₜ,这导致了图 5 中看到的更模糊的输出。
图 5。使用多个嵌入来表示目标的效果。如果我们使用前 3 个 zₐ嵌入,则输出是压缩的,当我们使用全部 8 个时,输出是 AEI 网。改编自[1]。
AAD 发电机
AAD Generator 是“自适应注意反规范化生成器”的首字母缩写。它按照增加空间分辨率的顺序综合前两个子网络的输出,以产生 AEI-Net 的最终输出。它通过堆叠一个名为 AAD Resblock 的新块来实现,如图 6 所示。
图 6。左图为 AAD 发生器架构,右图为 AAD ResBlock。改编自[1]。
这个模块的新部分是 AAD 层。让我们把它分成 3 个部分,如图 7 所示。第 1 部分从较高的层面告诉我们如何编辑输入要素地图 hᵢₙ,使其在属性方面更像 Xₜ。具体地说,它输出两个与 hᵢₙ大小相同的张量;一个包含将与 hᵢₙ中的每个像元相乘的缩放值,另一个包含移位值。第 1 部分图层的输入是属性向量之一。同样,第 2 部分将告诉我们如何编辑 hᵢₙ的特征地图,使其在身份上更像 Xₛ。
图 7。AAD 层的架构。改编自[1]。
第 3 部分的任务是选择我们应该在每个单元/像素上听哪个部分(2 或 3)。例如,在与嘴相关的细胞/像素处,这个网络会告诉我们多听第二部分,因为嘴与身份更相关。这在图 8 所示的实验中得到了实证。
图 8。展示 AAD 层第三部分所学内容的实验。右图显示了整个 AAD 生成器在不同步长/空间分辨率下的第 3 部分输出。明亮的区域表示我们应该听身份(即第二部分)的细胞,黑色区域用于听第一部分。注意,在高空间分辨率下,我们主要听第一部分。改编自[1]。
因此,AAD 生成器将能够一步一步地构建最终图像,其中在每一步中,在给定身份和属性编码的情况下,它决定放大当前特征地图的最佳方式。
现在,我们有一个网络,AEI 网,可以嵌入 Xₛ和 Xₜ,并以一种实现我们目标的方式整合他们。我们将把 AEI 网的输出称为 Yₛₜ*.
培训损失
一般来说,损失是我们希望网络做什么的数学公式。训练 AEI-Net 有 4 个损失:
- 我们希望它输出一个真实的人脸,所以我们会像任何一个 GAN 一样有一个对抗性的损失。
- 我们希望生成的人脸具有 Xₛ.的身份我们拥有的唯一代表同一性的数学物体是 zᵢ.所以,这个目标可以用下面的损失来表示:
3.我们希望输出具有 Xₜ.的属性为此的损失是:
4.作者基于以下想法增加了另一个损失,即如果 Xₜ和 Xₛ实际上是相同的图像,网络应该输出 Xₜ:
我相信这最后的损失是必要的,以推动 zₐ实际编码属性,因为它不像 zᵢ.预先训练据猜测,如果没有它,AEI 网络可能会忽略 Xₜ,让 zₐ只生产零。
我们的总损失只是以前损失的加权总和。
听觉网络
AEI-Net 是一个完整的网络,可以进行面部交换。然而,它在保留遮挡方面不够好。具体来说,只要目标图像中有遮挡部分面部的项目(如眼镜、帽子、头发或手)应该出现在最终输出中,AEI-Net 就会将其删除。这样的项目应该仍然存在,因为它与将要改变的身份无关。因此,作者实现了一个称为“启发式错误确认细化网络”的附加网络,它具有恢复这种遮挡的单一工作。
他们注意到,当他们把 Xₛ和 Xₜ作为 AEI 网络的输入时,同样的图像仍然不能保持如图 9 所示的遮挡。
图 9。当我们输入与 Xₛ和 Xₜ.相同的图像时,AEI 网络的输出注意头巾上的链子是如何在输出中丢失的。改编自[1]。
因此,他们没有把 Yₛₜ和 Xₜ作为听觉网络的输入,而是把 Yₛₜ & (Xₜ -Yₜₜ)作为听觉网络的输入,其中 Yₜₜ是 AEI 网络的输出,而 Xₛ和 Xₜ是同一个图像。这将使听觉网络指向遮挡未被保留的像素。在图 10 中可以看到听觉网络。
图 10。听觉网络的结构。改编自[1]。
培训损失
HEAR-Net 的损失是:
- 保留身份的损失:
2.没有改变 Yₛₜ*的损失:
3.一个建立在这样一个事实上的损失,如果 Xₛ和 Xₜ是相同的图像,那么听觉网络的输出应该是 Xₜ:
总损失是这些损失的总和。
结果
变脸者的结果是惊人的。在图 11 中,您可以找到一些它对数据集之外的图像(例如来自野外的图像)的泛化性能的例子。请注意它是如何在不同的艰苦条件下正确工作的。
图 11。结果证明了 FaceShifter 的卓越性能。改编自[1]。
参考
[1] 李,鲍,杨,陈,文,人脸变换:实现高逼真度和遮挡感知的人脸交换(2019),Arxiv .
了解快速 R-CNN 和用于目标检测的更快 R-CNN。
让我们详细了解这些基于区域提议的卷积神经网络的最新技术。
使用 Canva 设计
我在上一篇文章中已经详细讨论了对象检测和 R-CNN。你可以在这里阅读以获得更好的直觉。
人们需要了解这些用于对象检测的最先进的模型,这些模型随着时间的推移而发展,现在被认为是当今更强大的网络的强大基础。
让我们进入主题。
因此,使用 R-CNN 版本进行对象检测存在一些缺点。它们是:
- 它消耗大量的时间、存储和计算能力。
- 它有一个复杂的多阶段训练管道(3 个阶段——对数损失、SVM 和 BBox 回归器的 L2 损失)。
在 R-CNN 一年后开发的快速 R-CNN ,非常有效地解决了这些问题,在测试时间内比 R-CNN 快 146 倍。
快速 R-CNN
R-CNN 中使用的选择性搜索为每个图像生成大约 2000 个区域提议,并且每个区域提议被馈送到基础网络架构。这意味着,对于单个图像,将有 2000 次向前传递。考虑用 1000 幅图像的数据集来训练网络。那将是 2M 总共(2000 * 1000)次向前传球这是巨大的!****
所以,快速 R-CNN 是基于一个想法,
为什么不考虑每张图片运行一次 CNN,然后找到一种方法在 2000 个提案中共享计算结果?
在快速 R-CNN 中,图像只被传送到底层 CNN 一次,而选择性搜索则照常运行。然后,通过选择性搜索生成的这些区域提议被投影到由 CNN 生成的特征地图上。这个过程叫做 ROI 投影(感兴趣区域)。
在开始 ROI 投影之前,需要了解一下子采样率。它是特征图尺寸与图像原始尺寸的比值。举个例子,
使用 Canva 设计
ROI 投影的思想是我们从 ROI 提议中获得边界框的坐标,并且我们需要通过相对于二次采样比率投影 ROI 提议来将它们投影到特征图上。
使用 Canva 设计
上图看起来有点复杂,但事实并非如此。考虑一个大小为 688 x 920 的图像被传送到 CNN,CNN 的二次采样率是 T2 的 1/16。所得到的特征图的大小导致了43×58(688/16x920/16)。类似地,ROI 提议的大小320×128,在二次采样之后导致20×8。通常,边界框的坐标以两种方式表示。
- 盒子中点的坐标(X,Y),宽度,高度。[X,Y,W,H]
- 边界框的对边坐标。[X1,Y1,X2,Y2]
这里,我们考虑第一个符号。从图中可以看出,ROI 建议的中点是 (340,450) ,这导致了特征图中的 (21,28) 。以这种方式,ROI 提议被投影到特征图上。
在网络中使用固定大小的图像的主要原因是因为完全连接的层。这些期望固定大小的向量,因为分配了固定的权重。这是网络不接受可变尺寸图像的主要原因。为了解决这个问题,Fast R-CNN 的作者想出了一个想法 ROI Pooling 其中特征图上的 ROI 投影被分成固定的维度。
使用 Canva 设计
如上所示,将红框视为 8 x 8 特征图上的 ROI 投影。假设我们需要一个 2×2 尺寸的输出。那么如果有奇数个维度,就不可能将它们等分。在这种情况下,我们将四舍五入到最接近的值。如图所示,假设我们得到一个 5 x 4 大小的提案。为了将其转换成固定尺寸的盒子,我们将高度和宽度除以所需的高度和宽度,即 5/2×4/2 = 2.5×2。对它们取整,任何一种方法都给出 2 x 2 和 3 x 2 。然后对每个块进行最大池化,并计算输出。这样,对于任何大小可变的区域建议,我们都可以获得固定维度的输出。所以现在,输入的大小没有限制。
使用 Canva 设计
因为还涉及到第三个维度,即特征地图中的深度,你可以这样想象,就像在一副卡片上戳一个洞。如上所述,您必须以相同的方式将 ROI Pool 应用于每个切片。
使用 Canva 设计
在 Fast R-CNN 中,我们使用一个 7 x 7 的网格进行池化。我们还去掉了最后一个池层。ROI 合并应用于最后一个卷积层的特征图。上面的例子是针对 AlexNet 架构的。
使用 Canva 设计
RCNN 的变化是,他们已经摆脱了 SVM 分类器,而是使用 Softmax。用于 Bbox 的损失函数是平滑 L1 损失。快速 RCNN 的结果是速度的指数增长。在准确性方面,没有太大的改进。这种架构在 PASCAL VOC 07 数据集上的准确率为 66.9% 。这里的总损失是分类损失和回归损失的总和,并且网络用单个损失反向传播,由此我们可以摆脱多阶段训练问题。
结果:
使用 Canva 设计
更快的 R-CNN
即使有了所有这些进步,在快速 R-CNN 过程中仍然存在一个剩余的瓶颈,即区域提议器。众所周知,检测物体的第一步是在物体周围生成一组潜在的包围盒。在快速 R-CNN 中,使用选择性搜索创建区域提议,发现一个相当慢的过程是整个对象检测过程的瓶颈。
因此,我们需要一种更好的技术,它给出少于 2000 个区域建议,比选择性搜索更快,与选择性搜索一样准确或更好,并且应该能够提出具有不同纵横比和比例的重叠 ROI。
从快速 R-CNN,我们知道区域提议依赖于已经用 CNN 的前向传递计算的图像的特征。
那么,我们为什么不将 CNN 的结果重新用于地区提案,而不是运行一个单独的选择性搜索算法呢?
首先,让我们了解不同纵横比和比例的重叠 ROI 的概念。
使用 Canva 设计
从图像中,我们看到许多物体相互重叠。我们看到一辆汽车,一辆自行车,一个人拿着一台电视机,电视机里还有一只狗。选择性搜索可以解决这个问题,但我们最终会得到大量的 ROI。我们需要想出一个能有效解决这个问题的主意。
如果我们在不同的物体周围画边界框,它看起来是这样的。
使用 Canva 设计
实现这个有点复杂,但是锚盒的想法让它变得简单。我们来了解一下这些锚盒是什么意思。
一般来说,对象可以放在正方形的盒子里,也可以放在长方形的盒子里,或者放在长方形的盒子里。概括地说,他们可以说是大,小,或中等大小。因此,通过实验发现,使用 3 种不同比例和 3 种不同纵横比的盒子可以检测到图像中的任何对象。
使用 Canva 设计
如果我们考虑上面的重叠图像,很有可能组合所有这些框,如上所示,会给你所有的重叠建议,但不是很准确。物体的主要部分将被覆盖。这可能是一种可以用来解决我们替换区域提议者的目的的技术。我们可以考虑一些替代方案,例如:
1.移除选择性搜索并在特征地图上应用滑动窗口。但有了这个,我们就能检测到大多数单一尺度的物体。
2.为了处理多种尺度,我们必须在输入端使用影像金字塔。但是使用 5 种不同比例的图像(几乎每个物体都可以被检测到)会使网络变慢 4 倍。
3.另一种选择是在特征图上使用不同大小的滑动窗口(如上图所示,9 个)。这个概念被称为特征金字塔。这包括在特征地图上使用 9 个不同大小的滑动窗口。
但是对于一个 600x1000 的图像,我们会有一个大约 40x60 的特征图。并且在每个位置使用 9 个不同的滑动窗口,对于特征图中的所有 40x60 值,我们最终得到 40x60x9 =~20,000 个建议。与仅提供 2000 个建议的选择性搜索相比,我们的建议几乎多了 10 倍。这将在计算上更加昂贵,并且还会有更多的误报。
4.考虑使用简单的 CNN BBox 回归器来代替选择性搜索,以获得图像的近似区域建议,该区域建议可以进一步馈送到底层的快速 R-CNN 架构。这是更快的 R-CNN 背后的核心思想。让我们再深入一点。
使用 Canva 设计
这个为我们提供近似 ROI 的网络被称为区域建议网络(RPN)** 。那么,我们如何设计这种 RPN,使其能够给出可以进一步处理的近似 ROI 呢?**
这里的想法是我们利用上面讨论的 9 个盒子。这些被称为锚盒或参考盒。在图像上的任何一点,我们都会得到大约 9 个不同比例和长宽比的不同边界框。
在特征图上使用大小为 3×3 的滑动窗口,并且在每个滑动窗口的顶部,我们将这 9 个框放置在中心,以检测各个窗口中存在的任何对象。让我们放大 RPN。
使用 Canva 设计
这就是我们如何从图像的所有区域得到不同的区域建议。9x 代表,该部分在 9 个不同的锚中重复 9 次。由此,我们得到所需的 ROI。但这里的问题是,我们又一次得到了大量的提议。
考虑同样的 40x60 特征地图的例子,我们再次避开 20K 建议(40 x 60 x 9)。这些盒子中最多有一个没有任何对象,这样的盒子应该被删除。为此,我们使用一个二元分类器**来预测盒子是否包含前景或任何背景。这样,所有包含背景的盒子都被去掉了。我们假设大概有 15K 的背景盒。我们还剩下 5000 多个盒子。由于分类在最后包括 max 层的 S ,我们得到每个框的置信度得分,指示该框内存在对象的概率。通过这种方式,我们可以根据置信度得分对盒子进行排序,并将前 n 个提议用作 ROI。n 可以相应地调整。**
此外,请注意,由于我们在 RPN 中不使用 ROI 池层,我们在那里使用卷积运算,其作用类似于滑动窗口,这里的 FC 层由卷积运算代替,类似于 R-CNN 的前身 Overfeat 的卷积运算。
使用 Canva 设计
因此,首先用一个 3×3 的窗口或过滤器对特征图进行卷积,这为我们提供了所需的锚点,然后对每个分类和回归部分进行 1×1 卷积。
使用 Canva 设计
总的来说,这是更快的 R-CNN 的架构。
更快的 R-CNN 是用 4 个损耗联合训练的:
- RPN 分类(对象前景/背景)
- RPN 回归(锚点→ ROI)
- 快速 RCNN 分类(对象类)。
- 快速 RCNN 回归(ROI →边界框)
结果:
使用 Canva 设计
结论
希望这能理清你对越来越快 R-CNN 的理解。在 2015 年 12 月,更快的 RCNN** 与主干网络 as ResNet -101 一起赢得了 COCO 物体检测竞赛,被认为是迄今为止最先进的物体检测模型。希望你今天学到了新东西!我将在接下来的文章中讨论更多关于 Mask R-CNN 和 Detectron2 的内容。**
如果你想取得联系,请在 LinkedIn 上联系我。
如果你想了解更多关于交叉熵的知识,你可以在这里阅读我以前的文章。
参考
- https://arxiv.org/pdf/1506.01497.pdf
- https://arxiv.org/pdf/1504.08083.pdf
- http://cs 231n . Stanford . edu/slides/2017/cs 231n _ 2017 _ lecture 11 . pdf
理解用于目标检测的快速 RCNN
快速递归神经网络综述
fast-RCNN 论文强调了 SPPNet 和 RCNN 的缺点,并建立了一个相对快速和准确的模型
瑞士伯尔尼——作者图片
Fast-RCNN 模型是通过克服 SPPNet 和 RCNN 的缺点而构建的。我已经写了关于这两个方面的文章,您应该在继续之前看一下:
R-CNN 的架构细节以及模型设计和论文的要点。
towardsdatascience.com](/understanding-regions-with-cnn-features-r-cnn-ec69c15f8ea7) [## 了解用于对象检测和分类的 SPPNet
SPPNet 允许可变大小的 CNN 输入图像,并可用于分类和对象检测
towardsdatascience.com](/understanding-sppnet-for-object-detection-and-classification-682d6d2bdfb)
该博客与上述两个博客的结构相同,即学生和教师之间的对话。
教师
我们之前看过 R-CNN 和 SPPNet。尽管这些模型表现得非常好,但它们都有一些缺点。以下是两种架构共有的缺点:
- 多阶段训练:首先在 ImageNet(预训练权重 us)上训练分类模型,然后针对检测数据集进行微调。在微调之后,softmax 被一个用于对象检测任务(在硬挖掘的数据上进行训练)的 one-vs-rest 分类器所取代。通过向最后一个池化图层的要素添加边界框回归器来提高性能。这是一个多阶段的过程,培训是一步一步进行的。
- 高空间和时间复杂度:在微调网络之后,并且在训练 SVM 和包围盒回归器之前,将特征缓存到磁盘以避免重复计算。生成这些特性需要大量时间,存储这些特性也需要数百 GB 的空间。
spp net 特有的缺点:
- 低效微调卷积层:与 R-CNN 不同,SPP 层很难更新它之前卷积层的权重。避免卷积层的微调会妨碍模型的性能。
所有上述缺点都在 Fast-RCNN 论文中得到解决。顾名思义,它是 RCNN 的一个相对快速的版本,并且利用了 SPPNet 的一些架构细节。
学生
R-CNN 论文中显示的深入分析和 SPPNet 层中引入的新颖 SPP 层让我非常惊讶,以至于我没有注意到任何这些缺点。您能解释一下 Fast-RCNN 中使用的模型架构吗?
教师
作者在 Fast-RCNN 论文中分析了三组模型:
- 小号(S): 卡芬内模型
- VGG _ 美国有线电视新闻网 _M_1024 (M): 与卡芬内相似的型号,但更宽
- VGG16 (L): 非常深的 VGG-16
我们将把我们的讨论限制在 VGG-16(预先在 ImageNet 上训练过),这是他们最深的网络。
Fast-RCNN 架构—论文
输入图像被发送到 VGG-16 并被处理直到最后的卷积层(没有最后的汇集层)。并且在这之后,图像被发送到小说的感兴趣区域(RoI) 池层。该池图层始终为最后一个卷积图层输出的每个要素地图输出一个 7 x 7 的地图。这个 7 x 7 的地图是通过池化产生的,其中窗口大小根据输入图像而改变。顺便说一句,来自该图的展平特征产生了与 VGG-16 的预训练 FC-6 层所期望的相同大小的特征向量。然而,最后的 1000 路 softmax 层被替换为 21 路 Softmax(与 RCNN 和 SPPNet 情况下的 SVM 不同)。另外对于边界框回归器,分支从最后一个 FC-7 层开始,而不是卷积层特征图。
注意:RoI pooling 层只是 SPP 层的特例,其中只使用了一个金字塔等级。在这种情况下(7 x 7)。此外,每个子窗口和步距的计算来自 SPPNet 论文。
学生
你上面提到的一个缺点是,SPP 层不能有效地反向传播。请你解释一下这个问题是如何解决的?此外,这种架构如何解决空间和时间复杂性等其他问题?
教师
当区域提议来自不同图像时,反向传播在 SPP 层变得无效。然而,他们提出了一种微调网络的有效方法。使用 N=2 个输入图像,并且对于每个图像,他们对每个图像采样 R=128 个 RoI。此外,他们用地面真实边界框拍摄 25% IoU 大于 0.5 的前景图像。为 IoU 与地面真值箱之间的间隔[0.1,0.5];这些建议被视为背景。提交人声称:
较低的阈值 0.1 似乎是硬示例挖掘的启发
注意:通过 RoI 合并层的反向传播具有与任何正常最大合并层类似的实现。作者已经用数学方法描述过了。更简单的理解,参考这个答案。
既然我们已经看到了反向传播在这个网络中是如何发生的,那么让我们也来看看多阶段训练的问题是如何处理的。作者没有单独训练,而是将包围盒回归器和 softmax 层一起训练。他们将这个损失函数命名为多任务损失函数。
多任务损失函数—按作者分类的图像
在上图中:
- 类预测(p): 每个 RoI 的离散概率分布[p = (p0,p1,p2 … pk)](包含 k+1 个类,其中 k = 0 为背景类)
- 类别标签(u): 是正确的类别
- 每次损失的权重(λ): 该值始终等于 1
- 艾弗森括号函数[u ≥ 1]: 这个赋值给其中一个类不是背景,否则为零。
- 预测边界框标签(t): t = (tx,ty,tw,th)给出所选 RoI 图像中 u 中每个类别的预测边界框元组。
- 基本事实包围盒标签(v): v = (vx,vy,vw,vh)给出了 u 中正确类的相应基本事实包围盒。
口头上,交叉熵损失用于训练最后 21 路 softmax 层,而 smoothL1 损失处理为 84 回归单元处理包围盒定位而添加的密集层的训练。这两个损失的总和用于微调剩余的网络,这与新的 softmax 和回归层的训练一起发生。
为了证实这种新的训练方法不会妨碍表演,本文给出了以下分析。
作者编辑的论文中的多任务训练分析
黄色框表示在训练或测试时没有边界框回归量的训练,而红色框表示使用多任务损失函数进行训练和微调后的结果。剩下的两列不言自明。需要注意的关键点是,使用这种多任务损失可以改善结果。
学生
他们使用的损失函数确实改善了 Pascal VOC 2007 上的地图。那么,是不是由于这种多任务训练,避免了缓存这些特性,从而节省了我们将它们生成并写入磁盘的时间?
教师
是的,这个观察是正确的。此外,作者还提供了一些关于提高速度的更多信息。据观察,将奇异值分解(SVD)应用于 FC 层,将运算分成两个矩阵乘法,并减少了计算时间。
奇异值分解:将一个矩阵分解成三个矩阵,其中一个对角矩阵夹在两个正交矩阵之间。对角矩阵表示特定轴上的方差,并且是降序排列的。因此,从对角矩阵中选择顶部的 t 对角值,意味着选择在输出中贡献最大的值,因为它们具有高方差。(如果你对 SVD 的直觉不熟悉的话可以看看这个系列视频上的 线性代数 然后再查看一下 这个视频 )
SVD 时序分析来自论文
虽然该模型比 RCNN 和 SPPNet 更快,但使用 SVD 可以在 mAP 下降最小的情况下缩短时间。对于上图,前 1024 个值选自 FC-6 层的 25088 x 4096 矩阵,前 256 个值选自 4096 x 4096 FC-7 层。下图显示了该型号在速度方面与其他型号相比的表现。
与另一个模型的时间比较— 论文
上图可以总结如下:
- Fast-RCNN 模型的训练速度比 RCNN 快 9 倍,预测速度比 RCNN 快 213 倍
- 快速 RCNN 也比 SPPNet 训练快 3 倍,预测快 10 倍,提高。
学生
该文件是否提供了对他们架构的任何分析?
教师
通过实现前面描述的硬挖掘策略,反向传播变得很容易实现。然而,在深层 VGG-16 网络中哪些层需要微调也进行了探索,并在下面进行了描述。
作者发现,对于 VGG-16,微调来自 conv3_1 的所有图层显著影响了地图。微调 Conv2 层时,训练速度下降,微调 conv1 层超过 GPU 内存。然而,训练 Conv 层后的结果显示,与未被训练的层相比,在 mAP (从 61.4%到 66.9%) 中有巨大的跳跃。因此,微调卷积层也变得至关重要,这是 SPPNet 的一个主要缺点。之前讨论的所有结果均从 conv3_1 开始微调。
作者将他们的模型与当代模型进行了比较,快速 RCNN 的性能优于它们。我已经展示了 pascal VOC10 的结果。pascal VOC 2007 和 VOC 2012 的结果可以在论文中看到。
Pascal VOC 2010 的结果— 论文
本文分享的其他一些观察结果如下:
- 作者分析了改变输入网络的建议数量的影响。据观察,增加区域提案并不一定会增加地图。
- 作者还尝试了在多尺度环境中的训练和测试,其中训练的规则保持与 SPPNet(从最接近 224 的尺度中选择的区域建议)的规则相同。他们还使用了与 SPPNet 相同的一套标尺,但将最长的一边修剪到 2000 像素。据观察,虽然精确度有所提高,但单秤处理在速度和精确度之间提供了最佳平衡。
论文中最重要的细节已经讨论过了,但是还是建议看一下论文。在阅读 Fast-RCNN 论文之前,请确保先阅读 RCNN,然后再阅读 SPPNet。
参考
R.Girshick,J. Donahue,T. Darrell,J. Malik,用于精确对象检测和语义分割的丰富特征层次,计算机视觉和模式识别,2014 年
K.何,X 张,s 任,孙军,用于视觉识别的深度卷积网络空间金字塔池,,2014
R.Girshick,快速 R-CNN ,ICCV,2015
理解使用相关矩阵和散点图进行特征提取
世界上的数据非常庞大,需要非常有意识地进行处理,以获得我们希望通过新的数据科学方法实现的任何合理结果。
在处理给定数据集中的大量要素时,本文将讨论一个非常基本且重要的概念。
在 Unsplash 上由 Carlos Muza 拍摄的照片
任何典型的机器学习或深度学习模型都是为了从大量数据中提供单一输出,无论这些数据是结构化的还是非结构化的。这些因素可能在不同的系数和程度上有助于所需的结果。需要根据它们在确定输出时的显著性,并考虑这些因素中的冗余,过滤掉它们。****
在监督学习中,我们知道总有一个输出变量和 n 个输入变量。为了非常清楚地理解这个概念,让我们举一个简单的线性回归问题的例子。
在简单的线性回归模型中,我们最终会从模型中生成一个 y=mx+c 形式的方程,其中 x 是自变量,y 是因变量。由于只有一个变量,y 必须依赖于 x 的值。虽然在实时计算公交车从 A 到 b 的平均速度时,可能很少有其他被忽略的外部因素,如空气阻力。这些因素肯定会对输出产生影响,但意义最小。在这种情况下,我们的常识和经验帮助我们选择因素。因此,我们选择司机给汽车的加速度,忽略空气阻力。但是在我们不知道输入变量对输出的重要性的复杂情况下呢?数学能解决这个难题吗?
是啊!这里就出现了关联的概念。
相关性是一种统计方法,表示两个或多个变量一起波动的程度。简单来说,它告诉我们一个变量对于另一个变量的微小变化有多大的变化。根据变化的方向,它可以取正值、负值和零值。因变量和自变量之间的高相关值表明自变量在确定输出时具有非常高的重要性。在有许多因素的多元回归设置中,必须找到因变量和所有自变量之间的相关性,以建立一个具有更高精度的更可行的模型。人们必须永远记住更多的特征并不意味着更好的准确性。如果更多的特征包含任何不相关的特征,从而在我们的模型中产生不必要的噪声,那么这些特征可能会导致精度下降。
两个变量之间的相关性可以通过各种度量来发现,如 皮尔逊 r 相关、肯德尔等级相关、斯皮尔曼等级相关等。
Pearson r 相关性是最广泛使用的相关统计量,用于测量线性相关变量之间的相关程度。任何两个变量 x,y 之间的皮尔逊相关性可通过下式得出:
n-观察次数,I-表示第 I 次观察
让我们考虑一下关于纽约、加利福尼亚和佛罗里达的新创业公司的数据集 50_Strartups。数据集中使用的变量是利润、R&D 支出、管理支出和营销支出。这里利润是被预测的因变量。
让我们首先对每个独立变量分别应用线性回归,以直观地显示与独立变量的相关性。
从散点图中,我们可以看到,R&D 支出和利润具有非常高的相关性,因此,与 R&D 支出相比,预测产量和营销支出与利润的相关性较低,具有更大的意义。
但是,管理和利润之间的分散表明,它们之间的相关性非常小,最终可能会在预测过程中产生噪声。因此,为了获得更好的结果,我们可以在模型中排除这个特性。
这个过程消除了我们模型中无关紧要和不相关的特性。但是,冗余功能怎么办?
冗余特征:虽然有些特征与我们的目标变量高度相关,但它们可能是冗余的。任何两个自变量如果高度相关,则被认为是冗余的。这造成了不必要的时间和空间浪费。甚至两个变量之间的冗余度也可以通过相关性找到。
注:因变量和自变量之间的高相关性是理想的,而两个自变量之间的高相关性是不理想的。
上面两张图显示了独立变量之间的相关性。我们可以在第一张图中看到较高的相关性,而在第二张图中看到非常低的相关性。这意味着我们可以排除第一张图中两个特征中的任何一个,因为两个独立变量之间的相关性会导致冗余。但是要去掉哪一个呢?答案很简单。与目标变量具有更高相关性的变量保留,而另一个被移除。
确定变量之间的相关性:
df = pd.DataFrame(data,
columns=['R&D Spend','Administration','Marketing Spend','Profit'])
corrMatrix = df.corr()
print (corrMatrix)
输出:输出显示一个 2*2 矩阵,显示所有变量之间的 Pearson r 相关性。
数据集中所有变量之间的相关性。
最后,基于它们的 r2 分数比较各种多元回归模型。
通过选择特征的组合来评分。
从实验分数中,我们观察到:
->相关性低的自变量导致 r2 得分较低。(例如:仅管理)
->相关性较高的变量在我们的模型中为我们提供了较高的 r2 分数(例如:R&D 支出和营销支出)
消除冗余变量或无关变量可能会/可能不会导致我们的精度损失可以忽略不计,但使它在许多约束条件下成为一个非常有效的模型。
供进一步参考:
数据集:【https://www.kaggle.com/farhanmd29/50-startups
代号:https://github.com/Tarun-Acharya/50_Startups_Regression
理解联合学习
关注机器学习中的隐私
图片来自皮克斯拜
随着对隐私的日益关注,联邦学习已经成为现代机器学习中的基本概念之一。联合学习旨在训练一个模型,而无需将个人信息或可识别数据上传到云服务器。你可能已经知道,机器学习模型需要大量的数据来训练。但有时训练数据是敏感的,人们越来越不愿意与第三方分享他们的个人数据。随着对隐私的日益关注,联合学习现在在大多数机器学习应用中是必不可少的。
数据诞生于边缘
全球超过 10 亿台边缘设备(如手机、平板电脑和物联网设备)不断产生数据。对于公司和开发者来说,这些数据可以通过训练更好的模型来使他们的产品和用户体验更好。通常,客户端将数据发送到服务器,模型在服务器上运行推理并返回预测。一旦模型返回预测,客户端就发送反馈,模型使用反馈进行自我修正。虽然这在收集数据和减少边缘设备上的计算负担方面具有优势,但它面临着关于离线使用、等待时间以及更重要的隐私的问题。
云上的模型推理。作者图片
虽然该模型可以在本地进行训练和推断,但它也有其局限性。单个用户提供的数据太少,无法对模型进行充分训练。在这种情况下,其他设备的数据不起作用,这导致了非一般化的数据。这已经成为隐私和更好的智能之间的斗争,直到最近几乎没有解决方案。
联合学习是如何工作的?
在联合学习中,服务器将训练好的模型(M1)分发给客户端。客户端根据本地可用的数据训练模型。然后,这些模型而不是数据被发送回服务器,在服务器中,它们被平均以产生新的模型(M2)。这个新模型(M2)现在充当主要模型,并再次分发给客户。重复这个过程,直到模型获得满意的结果。在每一次迭代中,模型都会变得比原来好一点。因此,联合学习产生了更好的智能,而用户的个人数据在他们的设备中是安全的。
联合学习。作者图片
预测我的下一个单词。
联合学习最常见的例子是谷歌的键盘应用 Gboard。机器学习模型改善了用户体验,如滑动打字、自动更正、下一个单词预测、语音转文本等。联合学习在这里发挥了巨大的作用,因为你输入的内容对你来说是非常私人的,你不会想把你的数据发送到服务器。本地模型使用您的数据进行训练,然后发送到服务器,就像来自不同客户端的许多其他模型一样。服务器取这些新模型的平均值来产生一个新模型。服务器现在向客户端分发最新的模型。这个过程永远重复。
安全协议
即使用户数据没有上传到服务器,也有可能对模型进行逆向工程以获取用户数据。模型聚合和客户端数据加密用于解决这个问题。
联合学习协议将模型输出进行组合和求和,并且服务器只能访问集合模型,而不能访问单个模型。这里,设备只报告计算所需的数据。服务器将该模型分发给所有客户端。
掩蔽用于在聚合期间从客户端消除相反的数据点。由于被屏蔽的值被发送到服务器,任何人都很难截取这些值并对任何个人数据进行反向工程。
张量流联邦
TensorFlow Federated(TFF)是由 Google 的 TensorFlow 团队开发的一个开源框架,用于分散数据的联合学习。TFF 仍处于起步阶段,有很多需要改进的地方。在撰写本文时,TFF 只提供本地模拟运行时,没有部署选项。
您可以使用 pip 软件包管理器安装 TensorFlow Federated。
pip install tensorflow_federated
TensorFlow Federated 提供了两套接口,即联邦学习(FL)和联邦核心。使用联合学习接口,开发人员可以实现联合训练或联合评估,开发人员可以将联合学习应用于现有的 TensorFlow 模型。联邦核心接口用于测试和表达新的联邦算法,并运行本地运行时模拟。
我将在下一篇博客中写更多关于 TensorFlow Federated 的内容。
结论
这个博客是对联合学习的一个快速总结,我将很快发表更多深入的博客。我打算写更多关于一些鲜为人知的机器学习概念的博客。关注我以获取最新更新。
你可以在 Twitter 、 LinkedIn 和 Github 上找到我。
理解自然语言处理中的框架语义分析
让计算机理解我们语言含义的尝试
帕特里克·托马索在 Unsplash 上的照片
研究计算语言学可能具有挑战性,尤其是因为语言学家创造了许多术语。它可以是任务的形式,如词义消歧、共指解析或词汇化。每个任务的属性都有术语,例如,词条、词性标签(POS 标签)、语义角色和音位。
这篇文章旨在用通俗的语言给出对框架语义解析任务的广泛理解。从它的用途、一些术语定义和现有的框架语义解析模型开始。本文将不包含对定义、模型和数据集的完整参考,而是仅包含主观上重要的东西。
框架语义解析任务始于 FrameNet 项目[1],其中完整的参考资料可在 its 网站 2获得。它旨在捕捉单词的意思。
语义框架解析用于什么?
语义框架解析可用于需要更深入理解单词含义的应用,如问答。它试图确定文本在谈论什么(框架的过度简化的解释)以及谁对谁做了什么(框架元素或语义角色的过度简化的解释)。考虑一个例子
香蕉价格上涨了 5%
香蕉价格上涨了 5%
[香蕉价格]上涨了[5%]
括号中的短语是论元,而“增加”、“上升”、“上升”是谓语。
所有这些句子的意思都一样,但是计算机怎么能理解它们呢?例如,我们希望能够问一台电脑,
"香蕉的价格上涨了多少?"
给定一个混合结构,它可能会感到困惑,找不到正确的答案。
但是如果这台计算机能把那些句子解析成语义框架呢?它将识别出这很可能是一个运动方向帧。然后,它将从与运动方向帧相关的帧元素中识别出[香蕉的价格]是主题并且[5%]是距离。知道了这一点,它应该用距离框架元素来回答。
术语定义
阅读文章和论文进行框架语义解析是令人困惑的。乍一看,阅读材料中的大多数术语很难理解。因此,理解一些核心术语是非常重要的。
框架(语义框架)
框架,或语义框架,是句子一部分的范畴。这个范畴表示句子的一部分会有某些成分。在某种意义上,语义框架就像一本规则书。当你看到句子的一部分有这个语义框架,你就知道那部分句子里可能还有什么了。让我给你看一个例子
西拉试图挥动她的剑来抵挡,但是剑太重了。
你会注意到剑是一种“武器】,她的(可以和西拉一起引用)是一种“持用者”。这句话很有可能被归类为包含“武器”框架(见框架索引)。根据“武器”框架,它必须具有“武器”元素。或者,它可能包含一个“持用者”角色,如本例所示。
目标
应该用框架标记的单词或单词序列。最好能看到一个例子。
图 1:经过框架语义解析的句子示例[4]
图 1 显示了一个有 4 个目标的句子示例,用突出显示的单词和单词序列表示。这些目标是“发挥”、“重大”、“预防”和“枯竭”。这些目标中的每一个都将直接对应于一个框架——执行者和角色、重要性、阻挠、成为干巴巴的框架,用方框来标注类别。
框架元素
框架元素是语义框架、的组成部分,特定于某些框架。这意味着如果你已经看到了帧索引你会注意到有高亮显示的单词。这些是框架元素,每个框架可以有不同类型的框架元素。
在图 1 中,框架元素用下划线表示。例如,“胡佛大坝”,“一个主要角色”,“在防止拉斯维加斯干涸”是框架演员和角色的框架元素。
但接下来你可能会想,哇!但这不就是整句话吗?那么一个框架能覆盖整个句子吗?这取决于每个特定的框架规则。我再给你举个短一点的例子,“拉斯维加斯”是 BECOMING _ DRY frame 的一个框架元素。看到了吗?它不必是一个完整的句子。
引理
词条是词的基本形式。在英语中, runs,ran,run 会有相同的引理: run 。一个引理并不确切地表示一个词,因为它可能包含不止一个词,例如,“原子武器”或“火焰喷射器”。
词汇单位
在这个上下文中,词汇单元是一对基本形式的单词(词条)和一个框架。在帧索引处,一个词汇单元也将与其词性标签配对(如名词/n 或动词/v)。我相信目的是明确说明这个引理指的是哪个意思(一个引理/词有多个意思称为多义)。
来源帧索引
现有模型
解决这一任务的典型管道是识别目标、分类哪个帧和识别参数。
建立自动框架语义标注的早期工作包括两个步骤:识别句子中框架元素的边界和框架标注[3]。这项早期工作使用了大量的语法特征作为框架标记的输入。短语类型(名词短语、动词短语和从句)、语法功能、位置、语态和中心词。该系统的最终结果在识别框架元素边界上达到 66%,在框架标注上可以达到 80%的准确率。
最近的一个模型使用了神经网络方法来进行框架语义解析,看看它们是否可以减少语法特征的使用[4]。他们将任务分成三部分:目标识别、框架标记和参数识别。
目标识别是确定哪些单词或短语要被标记的任务。目标也可以被称为谓语,它可以是名词或动词。他们使用 3 个特征来进行目标识别:记号、记号的词性和记号引理。它们还结合了使用手套表示的预训练的令牌嵌入和训练的令牌嵌入。然后,他们使用 bi-lstm 层,最后一层将用于预测下一个令牌是否是目标。
这里的帧标记任务与前面的方法意思相同。这里,作者建议使用基于 bi-lstm 的分类器。该模型应该至少采用标记、词条、词性标签和目标位置,这是早期任务的结果。
这里我觉得有趣的是论点识别部分。论元识别可能不是你们中的一些人可能认为的“论元”,而是指谓元结构【5】。换句话说,给定我们发现了一个谓语,哪些词或短语与它相连。本质上和语义角色标注【6】一样,谁对谁做了什么。主要区别是语义角色标注假设所有谓词都是动词[7],而在语义框架解析中没有这样的假设。
参考
[1] Baker,Collin F .,Charles J. Fillmore 和 John B. Lowe1998.“伯克利框架工程”,86 年。https://doi.org/10.3115/980845.980860.
2框架网项目网站。https://framenet.icsi.berkeley.edu。
[3]吉尔迪、丹尼尔和丹尼尔·茹拉夫斯基。2002."语义角色的自动标注."计算语言学 28 (3)。https://doi.org/10.1162/089120102760275983。
[4] Swayamdipta、Swabha、Sam Thomson、克里斯·戴尔和 Noah A. Smith。2017."用软最大间隔分段 RNNs 和句法支架进行框架语义分析."【http://arxiv.org/abs/1706.09528.
[5]https://en . Wikipedia . org/wiki/Argument _(语言学)。
[6]https://en.wikipedia.org/wiki/Semantic_role_labeling。
7 詹姆斯·马丁·茹拉夫斯基。2019.语音和语言处理第 3 版。https://web.stanford.edu/~jurafsky/slp3/。
理解全栈数据科学:Jupyter 笔记本中的 NLP 模型如何帮助阻止灾难,第一部分
自然语言处理
从研究到生产的灾害信息分类模型——模型的自动训练、测试和打包
So . S
紧急情况随时随地都可能发生。当我们穿越这个毁灭性的全球疫情时,我们现在见证了所有一线急救人员的绝对才华。事实上,派遣救援队只是他们已经很辛苦的工作的一部分。当灾难发生时,援助请求信息会从所有不同的渠道快速而猛烈地传来。来自不同渠道的所有请求救援的信息会很快淹没人类的劳动。
因此,需要一种自动且可靠的方式将救援请求消息快速分类到正确的对应信道中。一个自然的选择是用一些机器学习模型构建一个消息分类器。所以我们通常会拿起我们的 jupyter 笔记本开始开发,对吗?事实上,这只是这里所做工作的一部分。借助 Jupyter 笔记本电脑,我们可以创建引人入胜的数据视觉效果和易于阅读的模型表示,但这些模型只能被观看,不会产生任何实际影响。因此,能够将模型转换成生产就绪的代码库对于团队直接创建功能是至关重要的。在这篇博文中,我们将通过在 PaaS 平台 Heroku 上构建一个完全容器化的、持续集成的灾难消息应用程序来说明全栈数据科学实践。由于我们使用的数据集(图 8 灾难消息)在数据科学社区中相当有名,我们将主要关注用于生产模型的高级工具,如 Sklearn Pipeline、Pytest、Docker 和 CircleCI。为了避免这篇文章太长而无法阅读,我们将重点讨论这个模型的构建和打包过程
项目总体结构
由于会涉及到各种不同的文件,因此对项目结构有一个清晰的理解对项目的成功至关重要。在项目的根目录下,我们会有一个标准的.gitignore
文件和一个ReadMe.md
文件。还存在 4 个子目录,
- 。circle ci—circle ci 持续集成工具所需,包含
circleci.config
文件,用于自动化模型培训和部署工作流 - disaster _ messaging _ class ification _ model—包含机器学习模型包,该包将传入的消息预测到其适当的类别中
- disaster_messaging_app —包含 API 和 docker 文件,可用于训练好的机器学习模型,将由 circleci 部署
- 笔记本 —包含探索性分析笔记本
。circleci 和笔记本文件夹的用途非常明显。当我们构建这些模块时,我们将看看disaster _ messaging _ class ification _ model和 disaster_messaging_app 中有什么。
获取数据
我们将使用 Kaggle 的多类别灾难响应消息数据集,其中包含英语消息、原始消息、消息发送通道以及所有 37 种二进制编码的消息类别。一封邮件可以属于多个类别。前 5 行如下所示。
disaster_response_messages.csv 前 5 行
我们可以手动下载数据并将文件保存到数据文件夹中。然而,在一般实践中,为了确保我们总是从数据源获得最新的数据,我们将使用 shell 脚本来自动化数据提取过程。对于我们的项目,数据来自 Kaggle 的多语言灾难响应消息。该数据集包含从多个渠道提取的 30,000 条消息。一般过程如下,
- 从 Kaggle 的多重灾难响应消息中下载数据
- 解压缩下载的 zip 存档文件,并将 CSV 文件保存到同一目录
- 删除 zip 存档文件
- 通过将验证 CSV 和测试 CSV 中的内容复制到培训 CSV 中,将所有消息文件合并成一个文件(我们将在后面设置培训测试验证)
- 将培训文件重命名为
disaster_response_messages.csv
- 删除两个额外的 CSV 文件-验证 CSV 和测试 CSV
所有这些步骤都可以在终端外壳命令中完成,
使用这个 shell 脚本,上面的整个过程可以在下面一行中执行
sh fetch_data.sh
执行后,您将能够看到disaster_response_messages.csv
文件存在于disaster _ messaging _ class ification _ model目录的 data 文件夹中。现在我们已经有了数据,我们可以继续为这个机器学习模型构建、训练和设置预测管道。
构建模型
对于我们大多数人来说,构建模型应该是一个相对熟悉的过程。我们获取数据,将相关的机器学习框架导入到我们的 jupyter 笔记本中,训练模型,然后评估模型。在 sklearn pipelines 的帮助下,我们可以构建一个自包含的、稳定的、可复制的模型管道。由于这个disaster _ messaging _ classification _ model目录包含多个相关文件,我们将从查看该文件夹的结构开始。
在灾难 _ 消息 _ 分类 _ 模型子目录中,包含以下文件/子目录
- 配置 —包含模型和日志记录的配置文件
- 数据 —下载数据的保存目录
- 特征-包含创建模型管线中使用的特征的模块
- 模型 —包含机器学习模型的训练和预测脚本
- 脚本 —包含
fetch_kaggle_dataset.sh
shell 脚本的目录 - 测试 —包含确保机器学习模型稳定行为的 python 单元测试
- trained_model —用于保存训练好的机器学习模型 pickle 文件和性能报告的目录,该文件夹不提交,将由数据实用函数自动创建(托管在 utils 目录中)
- utils —包含用于数据预处理、模型构建和可视化创建的实用函数
- 可视化——包含构建显示在前端的单词云的
generate_visuals.py
。 - Makefile—circle ci 正在使用的重要自动化脚本。关于这个文件的更多细节稍后
- py project . toml—依赖定义文件,用于生成依赖锁文件(更多信息此处)
- ****poem . lock—调用
poetry lock
从pyproject.toml
文件生成的锁文件 - setup.py —项目打包定义文件,包含所有安装需求和安装包
数据预处理
为了将数据提取为模型可用的格式,我们将按照以下步骤提取 CSV 并将转换后的数据保存到 SQLite 数据库中。
- 从保存在数据目录的 CSV 文件中加载数据作为熊猫数据帧(用
load_data(*messages_filepath*)
函数定义) - 在加载的数据帧中添加一列指示数据串测试分割(用
train_test_split_label(*df*)
功能定义) - 将数据帧保存到 SQLite 数据库中(用
save_data(*df*, *database_filename*)
函数定义)
4.定义一个调用上述 3 个步骤的函数(用process_data()
定义)
由于原始数据相对干净,大多数数据转换操作将在我们创建标记化句子特征时执行(接下来)。
特征—标记器
要将原始文本转换为机器可读的单词标记,我们需要构建一个标记化器,删除停用词(如 this、that、I、am……),将单词按字母排序为原始格式,删除 URL 并去掉多余的空白。该模块在名为message_tokenizer.py
的文件中被构建为一个转换器。由于这个 tokenizer 转换器被用作一个特性创建器,我们将把它放在特性目录中。
模型管道—培训
该模型的流水线仅由 4 个步骤组成,
- 记号化——由我们上面定义的记号化器完成
- 计数矢量化—对所有句子进行字数统计,这由 sklearn 的计数矢量化工具完成
- TF-IDF-使用计数矢量化文本,可以使用 sklearn 的 TF-IDF 转换器计算 TF-IDF 特征
- Adaboost 模型——一旦创建了特性,我们就可以将模型作为管道的最后一步
上述 4 个步骤可以很容易地构建成如下所示的管道函数。
作为下一步,我们可以继续构造训练函数,它包括如下步骤
- 从数据库加载训练数据(在
model_utils.py
的load_data_from_db
功能中定义) - 构建模型管道(在上述函数
build_model_pipeline()
中定义) - 符合模型
- 保存模型管线(在
model_utils.py
的save_pipeline
功能中定义) - 从数据库加载测试数据(在
model_utils.py
中定义) - 评估模型(在
model_utils.py
中定义)
load_data_from_db
函数可以选择加载训练数据还是测试数据(默认为“train”),具体定义如下
save_pipeline()
功能将以config.py
中定义的特定命名约定( model_name_version.pkl )将新训练的模型保存到特定位置( trained_models 目录),同时删除旧版本的模型管道以避免引用冲突。具体定义如下所示
evaluate_model()
函数将使用内置的 sklearn 评估 API 并生成性能报告,该报告将保存在trained _ models/performance _ report目录中。具体定义如下所示
模型管道—预测
在对模型进行训练、保存和评估之后,我们现在就可以使用该模型在新数据到来时生成一些预测。预测脚本只包含 3 个步骤,可以在一个函数中实现
- 收集输入数据并将其转换为与训练数据相同的形式
2.加载管道并使用输入数据进行预测
3.组装包含预测结果和模型版本的有效负载
具体定义如下所示
可视化——文字云
我们将建立将在前端使用的单词云可视化。由于我们的前端只使用 javascript 来显示单词云视觉效果,我们将依靠 Plotly 的特殊布局结构和plotly_wordcloud
函数来生成我们的消息来自的 3 个不同渠道中最常用的单词的 3 个视觉效果——新闻、社交媒体和直接消息。
总体步骤如下,
- 使用
visual_utils.py
中定义的load_data_from_db_visual
将保存的数据库中的信息数据作为数据帧加载 - 通过数据帧的
channel
列分割数据帧,并获得具有特定通道的子数据帧 - 使用
plotly_wordcloud
函数生成一个可以被前端解释的 JSON 格式的字符串
VisualsGeneration
类定义如下。
plotly_wordcloud
功能(配方由 Prashant 提供,此处为)遵循以下一般步骤。
- 创建单词云对象
- 创建 5 个列表,收集列表中每个单词的单词、频率、字体大小、位置、方向和颜色列表,从单词云对象的
.layout_
属性中提取 - 创建布局和数据跟踪,然后使用 Plotly 内置包构建图形
该函数定义如下:
自动训练/打包
随着模型管道、训练和预测脚本的构建,我们可以在终端中使用一系列 shell 命令来执行与该模型相关的工作流。例如,我们可以在终端中调用以下两个命令来打包保存的训练模型,并将模型上传到 PyPI 服务器。
python setup.py sdisttwine upload --skip-existing dist/*
执行模型打包将需要存在训练数据、所有安装在当前执行环境中的依赖包,并且模型已经被训练和保存。换句话说,在我们最终上传训练好的模型之前,我们需要完成一系列的步骤。
构建依赖关系定义
我们将从为我们的模型包创建适当的环境定义开始,这可以通过构建由 python 依赖管理工具poem使用的pyproject.toml
文件来完成。pyproject。toml 文件如下所示,
然后,我们可以在终端中调用poetry lock
,这将生成一个poetry.lock
文件,其中包含该项目所需的所有锁定版本的包依赖项。锁定版本将允许我们在安装包依赖关系时有必要的一致性,以防部分依赖包收到更新,这会给我们的项目带来意想不到的问题。
建立包装定义
对于我们上传到 PyPI 服务器的每个 python 包,我们需要创建一个setup.py
文件。幸运的是,我们可以使用名为 depshell 的工具为我们创建文件。我们只需要调用下面的命令来创建setup.py
文件。
pip install dephelldephell deps convert
创建的setup.py
文件如下所示
创建自动化生成文件
由于要执行这么多 shell 命令,简单地创建一个包含所有步骤的文件对于可读性和透明性来说并不理想。幸运的是,我们可以创建一个Makefile
,有效地将步骤聚集成组,这些组只包含完成单个任务的命令。例如,要安装和构建包依赖关系定义,我们可以将以下一系列命令写入 Makefile,
**poetry-install**:poetry lockpoetry config virtualenvs.create false \&& poetry export --without-hashes -f requirements.txt --dev\| pip install -r /dev/stdin \&& poetry debug
然后要执行这一系列的命令,我们只需要在终端中调用make poetry-install
,上面所有的命令集都会被执行。下面概述了将放入 Makefile 的步骤
- 从诗锁文件安装依赖项
- 从 Kaggle 获取数据集
- 从 dephells 构建 setup.py 文件
- 安装 nltk 资源(用于删除句子的停用词和词条)
- 通过调用
data_utils.py
预处理数据 - 通过调用
train.py
训练模型 - 通过调用
test.py
来测试模型 - 将模型包上传到 PyPI
完整的 Makefile 如下所示,
这很好,因为许多步骤可以总结成一个简短的 make 语句。为了进一步使整个过程自动化,我们将使用一个配置文件来指示 circleci 一个接一个地执行这些步骤。
配置 CircleCI 持续集成工具
CicleCI 是一个强大的持续集成工具,它允许我们的模型被自动训练、测试和打包,以供发布。它还使用自己的托管 docker 映像,模拟其他人有效使用您的项目的过程,因为它遵循本地安装项目时相同的设置步骤。我们只需要在名为.circleci
的指定文件夹中定义一个config.yaml
文件。更多关于 circleci 的资源和文档可以在这里找到。
所以,现在的问题变成了我们需要遵循什么步骤来使这个项目从零开始工作?我们已经有了 Makefile 中的大部分步骤,我们只需要连接它们。
- 安装 pip 工具/将 pip 工具升级到最新版本
- 安装诗歌工具(通过 pip 安装)
- 使用上面定义的 make 命令安装诗歌锁文件中的依赖项
- 使用上面定义的 make 命令从 Kaggle 获取数据集
- 使用上面定义的 make 命令预处理原始数据
- 使用上面定义的 make 命令训练模型
- 使用上面定义的 make 命令运行模型测试
- 初始化 PyPI 的凭证文件——这将允许我们将包上传到 PyPI 服务器
- 使用上面定义的 make 命令创建并上传模型包
circle 配置文件将以 3 行常规头开始,指示 circleci 的 python docker 映像版本,在我们的例子中是 0.2.1。
然后在下一行,jobs
将指示完成我们项目中定义的任务(我们上面定义的 9 个步骤)所要完成的自动化工作流步骤的开始。config.yaml
文件将如下所示。
设置 CircleCI
在用 yaml 文件配置 CI 工作流之后,我们将继续到 CircileCI 的网站并设置它与 github 存储库之间的连接。
我们将从使用包含我们的项目资源库的 github 帐户登录 CircleCI 开始。
然后,我们将导航到左侧菜单栏上的第二个图标(项目)并找到我们对应的项目存储库。对我来说,它叫做Disaster_Response_Messaging_Classifier
。然后,我们将单击蓝色的“设置项目”按钮。
在语言下拉菜单中,我们将选择“Python”,然后将我们的config.yaml
文件复制到下拉菜单正下方的编辑器中。然后点击“添加配置”按钮。
我们将登陆仪表板页面。由于我的项目已经构建了多次,工作流完成的状态将在仪表板上显示为行项目。要设置完成项目所需的环境变量,我们将单击左上角的“项目设置”按钮。
在下一页中,导航到左侧面板并单击“环境变量”。我们将需要添加以下 4 个环境变量,以使工作流完全发挥作用。
**KAGGLE_KEY**
—设置 Kaggle 的 API 时获得。关于如何设置的更多资源可以在这里找到。**KAGGLE_USERNAME**
—设置 Kaggle 的 API 时获得**PYPI_USERNAME**
—在 PyPI 服务器上注册账户时获得**PYPI_PASSWORD**
— 在 PyPI 服务器上注册账户时获得
其他 3 个环境变量用于部署,这将在第 2 部分中讨论。
我们现在已经为我们的项目设置好了持续集成工具,现在是时候看看它是如何工作的了。
提交、推送、观察和验证
如题所示。我们将提交并推动回购,并观察工作流程自行进行。如果一切正常,我们将看到以下屏幕,指示工作流已成功完成。
然后,我们可以登录到我们的 PyPI 服务器,验证包是否被正确地推送到主机中。它确实在我们的项目列表中,耶!
结论/结尾
我们现在已经成功地完成了机器学习模型的构建,该模型将帮助我们将消息分类到它们正确的通道中。更重要的是,我们已经完全自动化了该模型的模型构建、训练和打包过程。每次,当我们想要更改模型定义,或者甚至想要在有新数据进入时重新训练模型时,我们所要做的只是提交和推送。
谢谢你坚持到最后。这只是故事的一部分,因为我们将在第二部分中谈论一些更令人兴奋的部署过程,这些过程将使这个项目真正端到端。
理解甘斯:MNIST 案例研究。
这篇文章帮助你理解和发展关于这个算法如何工作的直觉。
在本文中,我们将介绍使用 GANs 生成手写数字(MNIST)的整个过程。如果你的目标是理解理论,获得直觉,那就继续读下去。如果你只是想学习如何编码,你可以在 Jupyter 笔记本上找到。此外,我们假设深度学习和机器学习概念的最低背景。如果你对一般的深度生成模型感兴趣,我将很快发表我的学士论文(查看我的生物网页,这是论文的一部分),其中调查了 4 个最常用的模型并对它们进行了比较。现在我们开始吧!
我们将定义一个生成新图像的代理( G ,生成器),另一个( D ,鉴别器),用于鉴别图像是真是假。这两个代理将在接下来的游戏中进行几轮较量。每一轮将包括以下步骤。
- g 生成新的图像。
- d 将被给予几个图像,包括生成的和真实的。
- d 会把这些图像分为真假。
- g 和 D 将从他们所犯的错误中吸取教训,改进他们的方法/系统。
遵循这个过程,G 和 D 会越来越强。请注意,如果它们最终是完美的,生成的图像将无法与真实的图像区分开来,D 将无法辨别真假。一旦发生这种情况,G 将是我们正在寻找的采样器。
生成器和鉴别器代理
我们将使用简单的神经网络来定义这些代理。值得注意的是,CNN 可能会提供更好的图像结果,但我们希望保持简单,更多地关注 GANs 的核心组件。
鉴别器将学习从图像空间到[0,1]空间的映射。该输出将被解释为图像是真实的概率。另一方面,生成器将学习从潜在空间到图像空间的映射。网络架构如下:
所使用的图像大小为 784,2 个隐藏层为 256,鉴别器输出层为 1,潜在空间为 64。
我们如何用这种结构对新图像进行采样?我们将在潜在空间(多元正态分布 q(z) )中定义一个先验分布,我们将从该空间中对噪声进行采样。然后,我们将使用生成器对其进行转换,以获得所需的图像。
z ~ q( z ), x = G( z )
在下图中,我们可以看到网络的结构。我们将在下一节看到它们是如何交互的。
甘斯结构。
培养
前面描述的博弈可以正式写成一个极小极大博弈:
最初由 Goodfellow [1]提出。
现在,让我们看看为什么这和游戏是一样的。如果鉴别器识别出 x 为真(D( x ) = 1),V 函数的第一项为 0,如果识别出为假(D( x ) = 0)。因此,由于图像来自真实分布,D 的目标是最大化它(将真实图像分类为真实)。如果鉴别器发现它是假样本,第二项为 0,如果生成器欺骗了鉴别器,则为-∞。现在,D 想要最大化,G 想要最小化。所以这个博弈中生成器的目标是最小化 V(D,G)而鉴别器的目标是最大化,就像公式中写的那样。
训练中使用的算法如下:
从算法中可以看出,训练将在一个代理和另一个代理之间交替进行。注意一些事情:首先,我们可以通过改变阶跃函数来使用任何我们想要的优化算法。在这种情况下,我们使用了深度学习社区广泛接受的 Adam 算法2。第二,我们没有像极小极大游戏中那样优化生成器。这里,不是最小化 log(1-D(G(z))) 的期望,而是最大化 log D(G(z)) 。这是因为根据[1]的作者,它在训练中更稳定。
结果
我们运行了 300 个时期的算法,批次大小为 N = 32 。
从下图中可以看出,直到第 100 个纪元,发生器越来越欺骗鉴别器。然而,在这一点上,它开始慢慢减少,鉴别器越来越能够区分真假。重要的是要明白,这并不意味着发电机没有学习。这只意味着鉴别器比发生器学习的“更多”。然而,很明显,代理 G 有一些限制(非常简单的网络),这可能是它不收敛到完美采样器的原因。
GANs 结果。在右边可以看到分数。这基本上是 D(G(z))对于伪分数的期望,并且对于真实分数具有相同但真实的图像。另一方面,人们可以看到 G 和 D 想要最小化的损失。回想一下:本来 G 想最小化,D 想最大化。然后,我们改变了 G 最大化另一个值的目标。在那之后,我们将目标转换为负数,这意味着它们都最小化。
还要注意,我们讨论的是 70% 对 30% 的准确性,这意味着生成器平均会欺骗鉴别器 10 次中的 3 次——完美是 10 次中的 5 次,这意味着鉴别器没有线索。现在,鉴别器能够在生成的图像中识别出与真实图像不同的模式,但是我们人类能够做到吗?在下图中,您可以看到不同训练阶段的样本。我们发现的有趣结果是,人类感觉不到生成和真实之间的任何差异[3]。对于手写数字来说,这可能并不令人惊讶,但是这些算法也用于面部生成,例如,这种结果更加强大。
GANs 生成的图像。左上是纪元 50、右上 100、左下 200 和右下 300。
尽管我们看到结果令人惊讶,但我们必须记住,直方图对人眼来说也是完美的,但它不会实现我们的概括目标,它只是记忆。为了测试我们是否过度拟合,我们将搜索每个生成图像的最近邻。如下图所示,生成的影像(左侧)与其在数据集中的最近邻影像(右侧)之间存在明显差异。我们不能断定我们没有过度适应,但至少我们看到我们正在学习一些有意义的东西。
左边是生成的图像,右边是根据欧几里德距离对应的最近邻。
现实情况是,从潜在空间到图像空间的映射是连续的,这意味着在一个空间中接近的图像在另一个空间中也会是接近的。现在,由于我们使用多元正态作为先验分布(单峰),如果我们在潜在空间中采样 2 个向量并对它们进行插值,则得到的线也将是非常可能的,这表明图像也应该是手写数字。这一切意味着这个插值的图像应该是不同手写数字之间的连续平滑变换,如下图所示(插值:9 → 7 → 1)。这在本图中有进一步的探讨:
在右图中,我们可以看到使用 g 进行空间转换的真实情况。在本例中,我们可以看到如何从单峰分布变为多峰分布,这基本上是现实中正在发生的事情,但在更高维度中,每座“山”都是不同的数字。此外,如果我们在潜在空间中采样 2 个点,对它们进行插值,并投影图像,可以看到会发生什么。右边的图是在 2 个不同点之间的潜在空间中的插值结果,从先前采样(多元正态分布)。
更多细节请访问 GitHub 中的代码。我的学士论文将很快在我的网站上发布,你也可以看看。
参考
[1] Goodfellow,I .、Pouget-Abadie,j .、Mirza,m .、Xu,b .、Warde-Farley,d .、Ozair、…、Bengio,Y. (2014)。生成对抗网络。在神经信息处理系统的进展(第 2672-2680 页)。
2金马博士和巴律师(2014 年)。亚当:一种随机优化方法。arXivpreprint arXiv:1412.6980。
[3] Salvia,v .,Sanfeliu,A. (2020)。图像生成的最新深度生成模型分析。(这是我的学士论文,很快就可以在我的网页上看到。)
https://github.com/VictorSP17/Simple_GAN_MNIST(在这里你可以找到这份文件的代码。)
[5]https://github.com/jsuarez5341/GAN-MNIST-Pytorch
理解 Python 中的生成器表达式
技术的
通过利用一个优雅且内存高效的解决方案在 python 中生成序列类型,提高您的 python 编程语言技能。
介绍
本文是对 Python 编程语言中的生成器表达式(gene XP)的介绍。
本文面向所有级别的开发人员。如果你是初学者,你可以学习一些新概念,比如生成器表达式、列表理解(listcomps)和序列类型生成。中级开发人员可以学习一两件关于可伸缩性和内存效率的事情,或者只是将本文作为复习。
在本文中,您会发现以下内容:
- 生成器表达式描述
- 如何利用生成器表达式(代码)
- 生成器表达式相对于其他类似解决方案的优势
- gene XP 和 listcomps 的内存和时间测量
生成器表达式
enexps 是在 python 中生成序列类型(如数组、元组、集合)的优雅且节省内存的解决方案。
生成器表达式类似于list comprehensions(list comps)——在 python 中构造列表序列类型的另一种方法。Genexps 和 listcomps 在实现方式上有相似之处。
下面的短代码片段描述了它们的语法相似性:
在下面的代码中,生成器表达式用于计算按整数递增的一系列值的总和。
#Generator Expression
accumulated_gexp = sum((1 + x for x in range(2000000)))
print(accumulated_gexp)
>> 2000001000000#List Comprehension
accumulated_listcomp = sum([1 + x for x in range(2000000)])
print(accumulated_listcomp)
>>2000001000000
虽然很微妙,但上面的 genexp 和 listcomp 示例之间的主要区别是 genexp 以方括号开始和结束,而 listcomp 以方括号开始和结束——更好的术语应该是'括号'。
下面是另一个使用生成器表达式创建元组的例子。
beginning_topic = ['Machine', 'Deep', 'Reinforcement']
ending_topic = 'Learning'tuple(print(beginning + " " + ending_topic) for beginning in beginning_topic)>> Machine Learning
>> Deep Learning
>> Reinforcement Learning
优势:内存效率
与 listcomps 相比,生成表达式更节省内存。Genexps 内存效率是利用 python 迭代器协议“产生或返回迭代器中的项目的结果。相比之下,列表理解为生成的列表及其内容利用内存。
一个生成器只会在需要的时候产生迭代器中的条目,因此给了 genexps 内存高效的特性。
如果上面写的还不够清楚,下面的代码片段会让它更加清楚。下面的代码片段显示了 genexps 和 listcomps 示例的内存大小要求(以字节为单位)。
from sys import getsizeofaccumulated_gexp = (1 + x for x in range(2000000))print(type(accumulated_gexp))
print(getsizeof(accumulated_gexp))
>> <class 'generator'>
>> 112accumulated_listcomp = [1 + x for x in range(2000000)]
print(type(accumulated_listcomp))
print(getsizeof(accumulated_listcomp))
>> <class 'list'>
>> 17632624
在上面的代码片段中,我们可以观察到 list comprehension 使用了 17632624 字节;而生成器表达式只使用了少得可怜的 112 字节内存。
还可以通过使用迭代器的 next 函数来访问序列的内容,用于生成器表达式和列表索引来理解列表。
print(next(accumulated_gexp))
>> 1print(accumulated_listcomp[0])
>> 1
优势:时间效率
与列表理解相比,生成器表达式的另一个优点是它们的时间效率特性。
对于许多开发人员来说,尤其是初学者,您更熟悉并接触到列表理解的使用。
但是在大多数情况下,使用生成器表达式的好处不是那么容易被忽略的,尤其是当程序执行的速度非常重要的时候。
在实际场景中,当你在一个序列上迭代一次时,你很可能会更好地利用 genexps。为了更大的灵活性和序列的多次迭代,您可能会使用 listcomps。
下面的代码片段演示了 listcomps 和 genexps 之间的执行时间差异。
import timeitgenerator_exp_time = timeit.timeit('''accumulated_gexp = (1 + x for x in range(200))''', number=1000000)
print(generator_exp_time)
>> 1.5132575110037578list_comp_time = timeit.timeit('''accumulated_listcomp = [1 + x for x in range(200)]''', number=1000000)
print(list_comp_time)
>> 29.604462443996454
使用 timeit python 模块,我们可以测量代码行的执行时间,如上所示。
正如我们所观察到的,生成器表达式用时不到两秒(generator_exp_time: 1.51…),而列表理解执行时间几乎多了 20 倍。
优势
下面总结了 python 中生成表达式的优点:
- python 中生成序列类型的高效内存方法。
- 为编写的代码增加了进一步的简洁性和可读性。生成器表达式被生成器函数缩短。
- 与列表比较相比,时间效率高。
结论
生成器表达式一点也不复杂,它们使得 python 编写的代码高效且可伸缩。
对于初学者来说,学习何时使用列表理解和生成器表达式是在职业生涯早期掌握的一个极好的概念。
我希望这篇文章对你有用。
要联系我或找到更多类似本文的内容,请执行以下操作:
- 订阅我的 YouTube 频道 即将上线的视频内容 这里
- 跟我上 中
- 通过 LinkedIn 联系我
使用小数据集从头理解梯度增强
什么是助推?
Boosting 是一种非常流行的集成技术,在这种技术中,我们将许多弱学习者组合起来,将他们转化为强学习者。增强是一种顺序操作,其中我们以渐进的方式建立相互依赖的串联弱学习器,即弱学习器 m 依赖于弱学习器 m-1 的输出。用于增强的弱学习者具有高偏差和低方差。简而言之,boosting 可以解释为 boosting =弱学习者+加法梳理。
梯度推进算法
梯度推进是一种用于回归和分类问题的机器学习技术,它以弱预测模型的集合的形式产生预测模型,通常是决策树(维基百科定义)
算法步骤:
作者图片
现在让我们在一个小数据集(D)上应用所有这些步骤。数据集的大小保持非常小,以避免冗长的计算。这是一个预测公寓价格(以 10 万卢比计)的数据集,使用的参数包括地毯面积、位置(位于城市黄金地段或城外)和停车场(我们有公寓停车场吗)数据集:
作者图片
第一步:
梯度增强的第一步是用常数值初始化模型 F0(x)。也就是说,我们必须找到γ(γ)的最佳值,以使损耗值降低。现在让我们为数据集(D)求解步骤 1 中的方程
作者图片
第二步:
这一步是我们建立所有弱学习者的循环。这里 m=1 表示我们正在构建第一个弱学习器,并且我们已经在步骤 1 中构建了基础学习器 F0(x)
第 2.1 步
这里我们首先计算伪残差,也称为假残差。与残差相比,使用伪残差的最大优点是,他们让我们有任何损失函数。也就是说,它们帮助我们最小化我们选择的任何损失函数,直到损失函数是可微的。大多数伪残差几乎与残差相似,或者与残差成一定比例。
以下等式显示了伪残差与实际残差是如何相似的。
作者图片
对于所使用的损失函数,伪残差完全等于实际残差,但这并不总是正确的,因为随着损失函数的改变,方程和值也改变。在上面的等式中,Fm(Xi)表示前一阶段来自弱学习者的预测值
算法步骤 2.1 中给出的方程与上述方程的 L.H.S .相同,用简单的英语来说,这两个方程都可以解释为损失函数相对于阶段 m-1 的模型构建的负导数
让我们计算数据集(D)的伪残差
作者图片
在上表中,rim 是实际值(yi)和模型预测值(F0(x))的差值,r 表示残差,m 表示我们正在制作的弱学习器的数量,I 表示实例的数量,因此这里 r11 表示实例 1 的弱学习器 1 的残差。
第 2.2 步
在这一步,我们训练下一个弱学习者 F1(X)。让本周的学习者成为决策树。
在梯度推进中,如果我们使用决策树作为弱学习器,那么决策树的深度应该在 2 到 9 之间,默认情况下设置为 3。此外,请注意,深度必须在 2 到 9 之间并不是一条经验法则,这在很大程度上取决于我们的数据集。
在梯度推进中,我们在 Xi 和伪残差上训练我们的弱学习者,即我们将使用地毯面积、停车场和位置来预测我们在步骤 2.1 中计算的伪残差。我们数据集的决策树
作者图片
决策树的叶子被称为终端区域(Rjm)。其中 m 是我们刚刚制作的树的索引,因为这是第一棵树,m=1,j 是树中每片叶子的索引。
第 2.3 步
在这一步中,我们确定每个叶子的输出值。在上面的树中,有一个具有 2 个条目的叶子,所以不清楚它的输出值应该是什么,所以叶子的输出值可以通过求解以下方程来计算
作者图片
注意:给定任何损失函数,叶的输出值总是该叶的残差的平均值。
第 2.4 步
在这一步,我们更新我们的模型。步骤 2.4 的等式有两个部分,第一部分显示我们对伽马(γ)的初始预测,即 F0(x),第二部分代表我们在步骤 2.3 之前刚刚构建的树。因此,该等式可以表示为
作者图片
在上面的等式中,alpha(α)代表学习率。α的值在 0 到 1 之间变化,设α= 0.1。梯度提升中的学习率参数用于控制每个弱学习器的贡献,这些弱学习器被串联添加。
Record1 :新预测= 45+0.1 *(10)= 44,与模型 F0(x)所做的早期预测相比,更接近真实值。这意味着我们朝着正确的方向迈出了一小步。
记录 2 :新的预测= 45+0.1*(15)=46.5,与之前模型 F0(x)的预测相比,更接近真实值。这意味着我们朝着正确的方向迈出了一小步
记录 3 :新的预测= 45+0.1 *(20)= 43,与模型 F0(x)的早期预测相比,更接近真实值。这意味着我们朝着正确的方向迈出了一小步。
Record4 :新预测= 45+0.1*(15)= 46.5,与模型 F0(x)做出的早期预测相比,更接近真实值。这意味着我们朝着正确的方向迈出了一小步。
现在我们已经完成了步骤 2 的第一次迭代。我们将继续迭代,直到 M 的值达到 M 的值,这里 M 是构建一个算法所需的弱学习者总数。实际上,对于梯度提升,M = 100。但是为了简单起见,让我们保持 M=2
现在我们将再次重复步骤 2。这意味着对于 m=2,我们将计算伪残差,拟合下一个弱学习器 F2(x),计算叶子的输出,并将更新模型。这里我们完成第 2 步得到的 m 的值达到了 m 的值
第三步
作者图片
在步骤 3 中,我们计算梯度推进算法的输出。上面的等式代表了我们算法的最终输出。当 m = 2 时,上述等式的第 3 部分表示步骤 2 中的树构建。这里 M = 2,这意味着 F2(x)是梯度增强算法的输出。
希望这篇文章能帮助你增加对梯度推进的理解。我们知道梯度推进是一种强大的机器学习算法,但这不应该阻止我们了解它是如何工作的。我们对算法了解得越多,我们就能更好地有效使用它,并解释它是如何做出预测的。
感谢阅读。
了解梯度下降和 Adam 优化
对亚当的直观看法,以及为什么它更常用于深度学习
来源:https://pix abay . com/插图/人工智能-大脑-思考-3382507/
人工智能在过去十年中如何影响了我们的日常生活,这是我们只能深思的事情。从垃圾邮件过滤到新闻聚类,指纹传感器等计算机视觉应用到手写和语音识别等自然语言处理问题,很容易破坏人工智能和数据科学在我们日常生活中发挥的作用。然而,随着我们的算法处理的数据量的指数增长,开发能够跟上复杂性增长的算法是至关重要的。一种给工业带来显著变化的算法是 Adam 优化程序。但是在我们深入研究它之前,首先让我们看看梯度下降和它的不足之处。
来源:https://www . research gate . net/figure/Sphere-function-D-2 _ fig 8 _ 275069197
如果你不知道什么是成本函数,我建议你先浏览一下这个博客,它是对这个主题的一个很好的介绍:https://medium . com/@ lachlanmiller _ 52885/understanding-and-calculating-the-cost-function-for-linear-regression-39b8a 3519 fcb
梯度下降
假设我们有一个如上所示的 2 个输入变量的凸成本函数,我们的目标是最小化其值,并找到 f(x,y)最小的参数(x,y)的值。梯度下降算法所做的是,我们从曲线上的一个特定点开始,使用负梯度找到最陡下降的方向,并在该方向上迈出一小步,不断迭代,直到我们的值开始收敛。
https://data science-爱好者. com/DL/Optimization _ methods . html
我个人觉得上面的梯度下降比喻非常酷,一个人从山顶开始,沿着能使他最快降低高度的路径向下爬。
来源:https://www . quora . com/What-turns-gradient-descent-into-gradient-ascent-in-deep-learning
同时给出了梯度下降的正式定义,我们继续根据需要执行更新,直到达到收敛。我们可以通过检查 f(X i+1) 和 f(X i) 之间的差值是否小于某个数字,比如 0.0001(如果使用 Python 实现梯度下降,则为默认值),来轻松检查收敛性。如果是这样,我们说梯度下降已经收敛于 f 的局部最小值。
如果你不能完全掌握梯度概念,或者对成本函数和梯度下降的更深入的知识感兴趣,我强烈推荐以下来自我最喜欢的 YouTube 频道 3Blue1Brown 的视频-
坡度下降不足的地方
要执行梯度下降的单个步骤,我们需要迭代所有训练示例,以找出特定点的梯度。这被称为批量梯度下降,并且已经进行了许多年,但是随着深度学习和大数据时代的到来,拥有数百万数量级的训练集变得很常见,这在计算上变得昂贵,执行梯度下降的单个步骤可能需要几分钟。通常的做法是所谓的小批量梯度下降,我们将训练集分成小批量,并分别使用这些批量执行梯度下降。这通常会导致更快的收敛,但这里有一个主要问题——我们在单步执行时仅查看训练集的一小部分,因此,该步可能不会朝着成本函数的最大下降方向。这是因为我们基于总数据的子集来最小化成本,这并不代表对整个训练数据来说什么是最好的。我们的算法现在不是沿着一条通向最小值的直线路径,而是沿着一条迂回的路径,甚至不总是通向最优,最常见的是超调(越过最小值)。
来源:https://engmrk.com/mini-batch-gd/
下图显示了 3 种不同批量情况下梯度下降的步骤,以及成本函数如何最小化的变化。在这两个图中,很明显,成本函数是最小化的,但是它是振荡的,尽管一般来说它是减小的。问题如下,我们是否可以以某种方式“平滑”梯度下降的这些步骤,以便它可以遵循噪音更小的路径并更快地收敛?答案,你可能已经猜到了,是亚当优化。
亚当优化算法
来源:https://medium . com/@ nishantnikhil/Adam-optimizer-notes-DDA C4 FD 7218
这里发生了很多事。我们来快速分解一下。首先,我们来看看涉及的参数。
- α——梯度下降步长的学习速率。
- β1 —动量步长的参数(也称为 Adam 中的一阶矩)。一般为 0.9
- RMSProp 步长的参数(也称为 Adam 中的二阶矩)。一般 0.99
- ϵ——数值稳定性参数。一般是 10^-8
- m,v——分别为一阶和二阶矩估计值。两者的初始值都设置为 0。
- t —偏差校正步长的时间步长参数。
- g 和 f——θ时的梯度和函数值。
Adam 本质上可以分解为两个主要算法的组合——Momentum 和 RMSProp。动量步骤如下-
m = beta1 * m + (1 - beta1) * g
假设β1 = 0.9。那么对应的步骤计算 0.9 *电流矩+0.1 *电流梯度。您可以将此视为最后 10 个梯度下降步骤的加权平均值,它消除了大量噪声。然而,最初,力矩设置为 0,因此第一步的力矩= 0.9 * 0+0.1 *梯度=梯度/10,依此类推。这一时刻将无法跟上原来的梯度,这就是所谓的有偏估计。为了纠正这一点,我们做了以下工作,称为偏差纠正,除以 1-(β1 的时间步长)
m_corrected = m / (1 - np.power(beta1, t))
请注意,1 次幂(β1,t)接近 1,因为 t 随着每一步变得更高,随后会降低校正效果,并在最初几步达到最大。
来源:https://medium . com/datadriveninvestor/指数加权平均深度神经网络-39873b8230e9
旁边的图表很好地描绘了这一点,黄线指的是用较小的β1(比如 0.5)获得的力矩(估计值),而绿线指的是更接近 1(比如 0.9)的β1 值
RMSProp 做了类似的事情,但略有不同
v = beta2 * v + (1 - beta2) * np.square(g)
v_corrected = v / (1 - np.power(beta2, t))
它还近似计算最后 1/(1-β2)个示例的加权平均值,当β2 = 0.99 时为 100。但它会计算梯度平方的平均值(一种缩放幅度),然后是相同的偏差校正步骤。
现在,在梯度下降步骤中,我们不用梯度,而是用如下的力矩-
theta = theta - learning_rate * m_corrected / np.sqrt(v_corrected) + epsilon)
使用 m_corrected 确保我们的梯度在总体趋势的方向上移动,并且不会振荡太多,而除以幅度平方的平均值的平方根确保步长的总幅度是固定的并且接近单位值。这也增加了自适应梯度,我不会详细讨论,这只是在我们接近收敛时改变步长的过程。这有助于防止超调。最后,ε被加到分母上,以避免在遇到梯度的估计值太小并被编译器四舍五入为 0 的情况下被 0 除。该值被故意选择为非常小,以便不影响算法,通常为 10^-8.量级
对性能的影响
自 2015 年以来,Adam 已被广泛用于深度学习模型。这是由 OpenAI 的 Diederik Kingma 和多伦多大学的 Jimmy Ba 在他们 2015 年 ICLR 的论文“Adam:一种随机梯度优化的方法”中提出的。听起来,亚当并不是以某人的名字命名的。它是“自适应矩估计”的简称。下图显示了将其应用于 MNIST 数据集上的神经网络模型时,与其他最小化算法相比的有效性。
来源:https://machine learning mastery . com/Adam-optimization-algorithm-for-deep-learning/
Adam 是最优化领域中最显著的成就之一。自从使用 Adam 以来,训练大型模型所需的几天时间已经减少到几个小时。自从它出现以来,它已经成为几乎所有深度学习库中使用的默认优化器。我自己经常使用 Adam—在一个手写数字分类问题上,我发现仅通过将我的优化器从小批量梯度下降更改为 Adam,我的训练准确率就从 79%跃升到 94%,所需的迭代次数减少到大约三分之一,考虑到我的训练数据大小约为 10,000,甚至不到 100 万,这是一个非常显著的变化,在这种情况下,效果会更加显著!
理解用于节点分类的图卷积网络
图解卷积网络(图片由作者提供)
神经网络在过去十年中取得了巨大的成功。然而,神经网络的早期变体只能使用常规或欧几里德数据来实现,而现实世界中的许多数据具有非欧几里德的底层图形结构。数据结构的不规则性导致了最近图形神经网络的发展。在过去的几年中,图形神经网络的不同变体正在被开发,图形卷积网络(GCN)是其中之一。GCNs 也被认为是基本图形神经网络的变体之一。
在本文中,我们将深入探讨由托马斯·基普夫和马克斯·韦林开发的图卷积网络。我也将给出一些关于使用网络 X 构建我们的第一张图的非常基本的例子。通过这篇文章的结尾,我希望我们能对图卷积网络内部的机制有更深入的了解。
如果你不熟悉图形神经网络的基本概念,我推荐你在这里阅读我之前的文章。
图中卷积神经网络
相同的权重(或核心,或 CNN 中的过滤器)应用于整个图像(作者的图像)
gcn 执行类似的操作,其中模型通过检查相邻节点来学习特征。CNN 和 GNNs 之间的主要区别在于,CNN 是专门构建来对规则(欧几里德)结构化数据进行操作的,而 GNNs 是 CNN 的一般化版本,其中节点连接的数量是变化的,并且节点是无序的(在非欧几里德结构化数据上是不规则的)。
2D 卷积神经网络(左)和图卷积网络(右)的图解,通过来源
GCNs 本身可以分为两种主要算法,空间图卷积网络和谱图卷积网络。在本文中,我们将关注 F ast 近似基于谱的图卷积网络。
在深入研究 GCNs 内部发生的计算之前,让我们先简要回顾一下神经网络中前向传播的概念。如果你熟悉的话,可以跳过下面的部分。
神经网络正向传播简要概述
全连接神经网络的图示(图片由作者提供)
在神经网络中,为了将特征表示传播到下一层(正向传递),我们执行下面的等式:
等式 1 —神经网络中的正向传递
这基本上相当于线性回归中的 y = mx+b ,其中:
m 相当于重量**
x 是输入特性**
b 是偏向**
上述正向传递方程与线性回归的区别在于,神经网络应用非线性 激活函数 来表示潜在维度中的非线性特征。
回头看上面的等式,对于第一个隐藏层(i = 0),我们可以简单地将等式重写为如下:
等式 2 —第一层神经网络中的正向传递
其中第 0 层的特征表示基本上是输入特征(X) 。
这个等式在图卷积网络中有什么不同?
快速近似谱图卷积网络
频谱 GCN 背后的最初想法受到信号/波传播的启发。我们可以把频谱 GCN 中的信息传播看作是沿着节点的信号传播。谱 gcn 利用图拉普拉斯矩阵的特征分解来实现这种信息传播方法。简而言之,特征分解帮助我们理解图的结构,从而对图的节点进行分类。这有点类似于主成分分析(PCA)和线性判别分析(LDA)的基本概念,其中我们使用特征分解来降低维度并执行聚类。如果你从未听说过特征分解和拉普拉斯矩阵,不要担心!在这个快速近似方法中,我们不打算显式地使用它们。
在这种方法中,除了节点特征(或所谓的输入特征)之外,我们将在前向传播方程中考虑邻接矩阵(A) 。 A 是表示正向传播方程中节点之间的边或连接的矩阵。在前向传递方程中插入 A 使得模型能够学习基于节点连通性的特征表示。为了简单起见,偏置 b 被省略。由此产生的 GCN 可以被视为以消息传递网络形式的谱图卷积的一阶近似,其中信息沿着图中的相邻节点传播。
通过添加邻接矩阵作为附加元素,前向传递方程将是:
等式 3—图形卷积网络中的前向传递
等待..你说 A,什么是 A ?*
A 是 A 的规格化版本。为了更好地理解为什么我们需要规范化 A 以及在 GCNs 中向前传递时会发生什么,让我们做一个实验。*
构建图形卷积网络
初始化图形 G
让我们首先使用 NetworkX 构建一个简单的无向图( G )。图 G 将由 6 个节点组成,并且每个节点的特征将对应于特定的节点号。例如,节点 1 的节点特征为 1,节点 2 的节点特征为 2,依此类推。为了简化,在这个实验中我们不打算指定边缘特征。
输出:
**
图形 G 可视化
由于我们只有 1 个图,这个数据配置是一个 单模 表示的例子。我们将构建一个学习结点要素表示的 GCN。
将邻接矩阵 (A)插入前向传递方程
下一步是从图 G 中获得邻接矩阵 (A) 和节点特征矩阵 (X) 。
输出:
现在,让我们研究如何通过将和插入正向传递方程来增加模型更丰富的特征表示。我们要对 A 和x进行点积运算,在本文中我们把这个点积运算的结果称为 AX 。
输出:
从结果可以明显看出, AX 代表相邻节点特征的总和。例如, AX 的第一行对应于连接到节点 0 的节点特征的总和,节点 0 是节点 1、2 和 3。这让我们了解了 GCNs 中的传播机制以及节点连接性如何影响 GCNs 所看到的隐藏特征表示。
邻接矩阵和节点特征矩阵的点积表示相邻节点特征的总和。
但是,如果我们想得更多,我们会意识到,虽然 AX 总结了,相邻节点的特征,但它没有考虑节点本身的特征。
糟糕,检测到问题!怎么解决?
插入自循环并规格化一个
为了解决这个问题,我们现在向 A 的每个节点添加自循环。添加自循环基本上是一种将节点连接到自身的机制。也就是说,邻接矩阵 A 的所有对角元素现在都将变成 1,因为每个节点都与自身相连。我们把 A 加上自循环叫做 A_hat 重新计算 AX ,现在就是 A_hat 和 X 的点积:
输出:
太好了!一个问题解决了!
现在,你可能会意识到另一个问题。 AX 的元素是未规格化。类似于任何神经网络操作的数据预处理,我们需要归一化特征以防止数值不稳定性和消失/爆炸梯度,从而使模型收敛。在 GCNs 中,我们通过计算 度矩阵 (D)并执行 D 的 逆 与 AX 的点积运算来规范化我们的数据
在本文中我们称之为 DAX 。在图形术语中,术语“度”是指一个节点所连接的边的数量。
输出:
如果我们比较 DAX 和 AX ,我们会注意到:
我们可以看到规范化对 DAX 的影响,其中对应于节点 3 的元素与节点 4 和 5 相比具有较低的值。但是,如果节点 3 与节点 4 和 5 具有相同的初始值,为什么在规范化后会有不同的值呢?
让我们回头看看我们的图表。节点 3 有 3 条入射边,而节点 4 和 5 只有 2 条入射边。节点 3 比节点 4 和 5 具有更高的度的事实导致节点 3 的特征在 DAX 中的权重更低。换句话说,节点的度越低,节点属于某个组或簇的能力就越强。
在论文中,Kipf 和 Welling 指出,进行对称归一化将使动力学更有趣,因此,归一化方程修改为:
让我们使用新的对称归一化方程来计算归一化值:
输出:
回头看看上一节中的等式 3,我们会意识到我们现在有了什么是 A 的答案!文中将 A* 称为重正化诡计。*
完成了特性处理之后,是时候完成我们的 GCN 了。
添加权重和激活函数
我们将使用 ReLu 作为激活函数来构建一个 2 层 GCN。为了初始化权重,我们将使用随机种子,这样我们就可以复制结果。请记住,权重初始化不能为 0。在这个实验中,我们将为隐藏层设置 4 个神经元。因为我们将绘制二维特征表示,所以将有 2 个输出神经元。
为了更简单,我们将使用 numpy 重写重正化技巧方程,只是为了更简单。
输出:
搞定了。我们刚刚建立了我们的第一个前馈 GCN 模型!
绘制要素制图表达
GCN 的“魔力”在于它可以学习特征表现,即使没有训练。让我们在通过 2 层 GCN 后可视化要素制图表达。
输出:
来自前馈 GCN 的特征表示
从上面的图中,可以清楚地看到有 2 个主要组,其中左边的组由节点 0、1、2 组成,右边的组由节点 3、4、5 组成。我们可以推断出 GCNs 已经可以学习特征表示,即使没有训练或者 反向传播 。
关键要点
- 图卷积网络中的术语“卷积”在权重共享方面类似于卷积神经网络。主要区别在于数据结构,其中 gcn 是 CNN 的一般化版本,可以处理底层非规则结构的数据。
- 在 GCNs 的前向传递方程中插入邻接矩阵( A ,使得模型能够学习相邻节点的特征。这种机制可以看作是沿着图中的节点传递消息的操作。
- **重正化技巧由 Thomas Kipf 和 Max Welling (2017) 用于归一化快速近似基于谱图卷积网络中的特征。
- gcn 甚至可以在训练之前学习特征表示。
感谢阅读!如果您想了解如何使用 CORA 数据集在节点分类任务上训练 GCN,可以阅读本系列的下一篇文章*。***
有什么意见、反馈或想讨论的吗?请给我留言。你可以在 LinkedIn 上联系我。
可以在GitHub上获取完整代码。**
参考
[1] T. Kipf 和 M. Welling,利用图卷积网络的半监督分类 (2017)。arXiv 预印本 arXiv:1609.02907。ICLR 2017
2 T .基普夫,https://tkipf.github.io/graph-convolutional-networks/
[3]吴,等。艾尔。,图神经网络综合研究 (2019)。
[4] T. S. Jepsen,https://towardsdatascience . com/how-do-deep-learning-on-graphs-with-graph-convolutionary-networks-7d 2250723780
用数据理解幸福动力学(上)
国家一级的描述性分析
在这些特殊时期,封锁给我们许多人留下了很多思考的时间。想想过去和未来。想想我们的生活方式和我们的成就。但最重要的是,想想我们对这个世界的贡献。当我在寻找灵感和能量时,我决定抓住幸福这个问题,并对其动力学的复杂本质有更深的理解。
什么能让人真正快乐?
我们都有自己对幸福及其关键决定因素的看法。有些人会选择财富,因为享受舒适喜欢高生活条件是必不可少的。有些人会拒绝这种唯物主义观点,并认为社会关系(家庭、朋友等。)和价值观才是最重要的。其他人会捍卫这一观点,即作为在遵守某些规则的同时受益于一系列权利的公民,真正决定我们生活条件并因此决定我们幸福的是政府及其政策。
这些都是直观的想法。但是现有的数据告诉我们什么呢?
从这个角度来看,我分析了《世界幸福报告》的数据,这是一项具有里程碑意义的全球幸福状况调查。更具体地说,它一方面包含基于盖洛普世界民意调查(GWP)收集的答案的幸福得分,另一方面包含经济生产、社会支持、预期寿命、自由、无腐败和慷慨等指标。
在两篇系列文章中,我将与您分享我的主要发现。第一篇文章旨在通过数据解释性分析,对各国的幸福和相关指标的演变给出一个总体概述。另外两个关注这些指标对幸福的因果影响。
结构:
- 关于数据
- 幸福概述
- 幸福&财富
- 幸福&社会环境
- 幸福&公共政策
1.关于数据
首先,让我们从定义将要使用的关键指标开始。来自《世界幸福报告》的数据特别有趣,因为它结合了基本的经济指标和更主观的社会指标。
经济指标
- 人均 GDP:根据购买力平价调整为 2011 年不变国际美元,来自世界发展指标(WDI) ,2018。
- 出生时健康预期寿命:来自世界卫生组织(世卫组织) 全球健康观察站数据仓库。
幸福指标
这些指标主要来自 GWP 的民意调查。它使用随机选择的、具有全国代表性的样本,持续调查 160 多个国家的居民。通常,在面对面或通过电话进行的采访中,会向每个国家的 1,000 名个人提出一系列问题。更多信息,可以看这篇文章。
研究幸福的主要挑战是,首先,如何衡量幸福。事实上,对幸福感的评估从对生活的总体判断(生活评价)到感受(日常情感)。在这种情况下,我们将使用 3 项指标:
- 坎特里尔生活阶梯量表(Cantril Scale)即生活阶梯量表(Life Ladder ):它要求受访者用 0 到 10 分的“阶梯”量表给自己的生活打分,其中 0 分表示最糟糕的生活。
- 积极情绪:这是来自 GWP 的前一天快乐、欢笑和享受的平均情绪指数。情感问题的一般形式是:“你在昨天的大部分时间里有没有体验过以下感觉?”
- 负面情绪:它代表前一天焦虑、悲伤和愤怒情绪的平均值。
社会指标
在社会支持方面,我们将分析两个指标:
- 社会支持:它代表了对 GWP 问题“如果你遇到了麻烦,你有亲戚或朋友可以在你需要的时候帮助你吗?”的二元回答(0 或 1)的全国平均值
- 慷慨度:它是对 GWP 对问题“你在过去的一个月里有没有给慈善机构捐过款?”的全国平均回答进行回归后的余数上人均 GDP。
最后,为了掌握人们对政治的看法对其幸福感的影响,我们将使用以下指标:
- 做出生活选择的自由:它代表了对 GWP 问题的二进制回答的全国平均水平“你对自己选择如何生活的自由感到满意还是不满意?”
- 对腐败的看法:它代表了两个 GWP 问题的二元答案的平均值:“腐败是否在政府中普遍存在?”和腐败在企业中是否普遍存在?”
- 对国家政府的信心,用类似的方法构建。
下面是对数据的快速描述:
- 年份包括:2005 年至 2019 年
- 国家数目:166 个
- 区域数量:10
2.幸福概述
总体幸福感很大程度上取决于经济猜想,正如人们所料。事实上,代表人们对自己生活评价的人生阶梯在金融危机前的 2006 年处于最低点。
图 1——人生阶梯的演变
正面影响和负面影响也是如此。这些指标给前一个指标带来了有趣的细微差别:它们分别指的是前一天快乐、欢笑、快乐或担忧、悲伤和愤怒的平均情绪。因此,与 Life Lader 相反,有一些短期的衡量幸福的方法。
在这种背景下,如下图所示,这些变量也处于金融危机后的最低点。然而,对负面影响的影响肯定比对正面影响的影响大。
图 2——积极和消极影响的演变
就地域差异而言,没有什么好惊讶的。平均而言,富裕国家更幸福。最重要的是,下图显示了世界各地存在的巨大不平等。
图 3——按地区划分的人生阶梯演变
图 4-2019 年按生活阶梯着色的地图
即使在同一地区,国家之间也存在很大差异,尤其是在南亚、中东以及非洲和撒哈拉以南非洲,如下图所示。相反,北美国家和欧洲国家的幸福水平非常相似。
图 5-2019 年各地区的生活阶梯差异
3.幸福与财富
几十年来,更多的财富是否会带来更多的幸福这个问题吸引了经济学家、行为科学家和普通大众的注意力。毫无疑问,人们的幸福和财富有很强的相关性。而且,数据并不与这一总体趋势相矛盾。其实如图所示,人均 GDP越高,幸福水平越高。
图 6-作为人均 GDP 函数的生活阶梯(2019 年)
然而,这条规则也有许多例外:人均 GDP 相似的国家的幸福水平会有很大差异。例如,挪威和科威特拥有相似的人均 GDP但是他们的幸福水平却有显著差异(1.3 分!).
即使在同一个地区,不同国家的幸福水平也有很大差异,尽管他们的人均 GDP 很接近。例如,芬兰和比利时的人均 GDP 相似,但他们的幸福水平相差 1 分!
图 7-西欧人均 GDP 函数中的生活阶梯(2019 年)
这些观察是否证明了“金钱买不到幸福”这句老话?实际上,他们指出了两个公认的观点:
- 总的来说,财富确实重要,但存在边际效用递减模式。这意味着一个人越富有,他财富的增加就越不会增加他的幸福。
- 用财富作为幸福的指标是有问题的。一方面,它可以用来表示人们在工作中取得成功和提高生活水平的意愿。因此,人们的财富多少反映了人们对目前生活的满意程度和对未来的希望。但是,另一方面,它不包括对人们的生活满意度有决定性贡献的其他变量,如家庭、工作和健康。
4.幸福和社会环境
当我们思考幸福的成分是什么时,几乎不可能排除社会因素。事实上,作为社会动物,我们需要爱、意义、来自我们与家人和朋友的亲密关系的支持,以及来自我们更广阔网络的归属感。
那么,社会关系是通向幸福的最佳途径吗?
我们来看一下数据。更具体地说,我们将关注以下变量:
- 社会支持:它衡量健康的社交生活能在多大程度上带来安全感。事实上,社交网络通常是人们建议的来源,当他们知道他们的家人或朋友可以在他们需要帮助时,会给他们带来安慰。
- 慷慨:它反映了一种归属感,这种归属感通常来自于给予他人和接受他人的行为。
下表包含每个区域的平均值。在社会支持方面,北美和西欧国家数值最高,而南亚国家最低。从慷慨度、北美、东南亚国家最高,而撒哈拉以南国家最低。不出所料,当生活阶梯和正面影响高而负面影响低时,这些变量高。
表 1 —每个地区的平均值
为了比较幸福和这些“社会”变量之间关联的强度,让我们来比较它们的相关性。下面是两个地区的相关矩阵,一个是高幸福水平的地区,西欧,,另一个是低幸福水平的地区,南亚。
图 8 —相关矩阵(左边是西欧,右边是南亚)
正如你所看到的,在西欧的人生阶梯、积极情感、和消极情感与社会支持和慷慨高度相关,而在南亚的则没有。我们能从中推断出什么?是因为国家的文化,政治制度,还是其他变量?需要进一步的研究来阐明这个问题。
关于哈佛成人发展研究的简短说明
1938 年,一组哈佛研究人员通过定期采访和体检跟踪一组十几岁男孩的发育情况,调查了快乐的问题。他们的目标是了解他们的健康和幸福随着时间的演变。它成为同类研究中最长、最著名的纵向研究之一。80 多年后,研究得出结论社会关系是人们幸福和健康的最重要因素之一。正如这项研究的现任负责人罗伯特·沃尔丁格在著名的 TED 视频中简单总结的那样:“那些保持温暖关系的人活得更久更快乐,而孤独者往往死得更早”。有关这项研究的更多信息,您可以阅读这篇文章。****
5.幸福与公共政策
毫无疑问,政府对人们的福祉有影响,因为它们实施的公共政策定义了重要的幸福因素,如获得教育、就业、医疗保健等。
事实上,人民的幸福与政府出台的政策之间的关系是一个微妙的问题,这在两个层面上:
- 政府在多大程度上有权干预以改变市场经济的社会效应?自由派倾向于主张慷慨的失业保护和福利,而保守派则捍卫政府最小干预以确保市场效率的理念。
- ****如何衡量政策影响?是否应该只关注国民经济增长、人均产量等客观指标?或者,我们是否应该退一步,反过来问,是什么样的公共政策让公民认为他们的生活更幸福?
历史上,一些国家把幸福放在他们理想的中心。
例如,美国独立宣言将追求幸福列为三种自然不可剥夺的权利之一,与生命和自由并列。它还明确指出这是任何政府的最终目标。
我们认为这些真理是不言而喻的:人人生而平等,造物主赋予他们某些不可剥夺的权利,其中包括生命权、自由权和追求幸福的权利
美国宪法,1787 年南亚国家不丹政府更进一步,将常规经济指标放在一边,主要关注国民幸福总值(GNH)** ,这是一个旨在衡量人口集体幸福和福祉的指数。与 GDP 相反,它的价值观包括与自然和谐相处和传统价值观。要了解更多信息,您可以阅读这篇文章,并观看这段简短的视频。**
在这里,我分析了人们对政府的看法对他们幸福感的影响。考虑了以下几个方面
- 通过对政府的信任,人们对政府及其政策的信心。
- 通过对腐败的认知,人们对该国腐败程度的认知。
- 人们在做出人生选择时的自由感,正如所表明的做出人生选择的自由。
- 医疗保健系统的效率大体上反映在出生时的健康预期寿命上。
下表包含每个地区的平均值。与其他国家(如北美国家)相比,中欧和东欧国家对国家政府的信任度和做出生活选择的自由度最低,对腐败的感知度*最高。*****
表 2—每个地区的平均值
为了比较幸福与这些“政策”相关变量之间关联的强度,我们来比较一下它们的相关性。下面是两个地区的相关矩阵,一个是快乐程度高的地区,北美,,另一个是快乐程度低的地区,*中欧。*****
****
图 9—相关矩阵(左侧为北美,右侧为中欧)
需要注意 3 个因素:
- 首先,对于北美的国家来说,生活阶梯与健康的预期寿命,做出生活选择的自由和,对国家政府的信任度,与对腐败的看法高度负相关。中欧国家的情况并非如此。
- 接下来,在北美国家,这些变量与正面和负面影响的相关性不如与人生阶梯的相关性显著。这表明这些“政策”相关变量会对人们对生活的总体判断产生更大的影响,而不是他们的日常影响。
- 最后,在中欧国家,做出生活选择的自由和积极和消极影响之间的相关性非常高。相反,这表明这一变量对人们的日常情感会产生更重要的影响,而不是他们对生活的总体判断。
下面的相关矩阵表明,发达国家和其他国家之间的这些差异适用于世界其他地区:
图 9 —中欧和东欧、北美、撒哈拉以南非洲和西欧的相关矩阵
同样,有 3 个要素需要注意:
- 生命阶梯和出生时健康预期寿命之间的相关性在北美非常高,在西欧国家不显著。这可能是因为西欧国家通常为其公民提供可靠的医疗保险。**
- 在北美国家,生活自由与生活阶梯的相关性明显高于其他国家。
- 对国家政府的信心和生活阶梯在北美和西欧有很高的正相关性,在这些地方,民主作为政治体制往往更为普遍。同样,对腐败的看法和人生阶梯在北美和西欧有很强的负相关性**
我们能从中推断出什么?很难从这些观察中得出任何结论,因为变量的分布在不同的地区有很大的不同。然而,上述相关性揭示了重要的事实,并为理解世界各地的幸福动态提供了有趣的见解。
结论
不出所料,幸福的动力是多方面的,因国家和时间的不同而不同。然而,这种复杂性不应阻止决策者考虑这些因素。相反,他们应该使用幸福指数来确定要实施的正确政策,同时也要衡量政策的绩效。他们可以依靠经济学、心理学、调查分析和健康领域专家的帮助,来定义如何有效地使用幸福感的测量方法
在这篇文章中,我简单地进行了数据解释性分析,以获得对幸福动态的深入了解。接下来,我将重点讨论幸福和其他因素之间的因果关系。
用机器学习理解幸福动力学(下)
对幸福驱动因素的深入分析
幸福是我们都渴望的,然而它的关键因素仍然不清楚。
有些人会认为财富是最重要的条件,因为它决定一个人的生活条件。然而,有些人会正确地指出,富人并不总是快乐的。相反,一个人的社会关系(家人、朋友等。)对他的幸福也有同样的贡献。最后,有些人会争辩说,政府实施的政策才是真正决定一个人幸福的因素,因为它们间接决定了我们的生活条件和社会交往。
这些都是关于幸福的一般和直观的想法,我们可以通过自己的经验很容易理解。然而,我认为数据驱动的方法可以让我们更深入地了解快乐的驱动因素。通过应用最先进的机器学习技术,将有可能定义最重要的因素,并定量衡量它们对一个人幸福的贡献。
在这种背景下,我分析了《世界幸福报告》的数据,这是一项具有里程碑意义的全球幸福状况调查。一方面,它包含了基于盖洛普世界民意调查(GWP)收集的答案的幸福分数,另一方面,它包含了经济生产、社会支持、预期寿命、自由、廉洁和慷慨等指标。
本文是一系列文章中的第二篇,在这篇文章中,我分享了我对该主题进行的深入研究的主要发现。在我之前的文章中,我从纯描述性的角度分析了幸福。因此,我讨论了幸福与三个基本要素之间的复杂关系:财富、社会关系和公共政策。这种数据探索性分析揭开了世界各地区之间长期存在的差异的面纱。
然而,它纯粹是描述性的。它没有告诉我们最关键的问题:什么真正带来幸福,到什么程度,为什么?本文旨在通过关注幸福和其他变量之间的因果关系,使用统计工具和机器学习来回答这些问题。
从这个角度来看,我依赖于两种方法:一种是经济学领域常用的“传统”方法,另一种是基于最先进的机器学习解释工具的新方法。
事不宜迟,让我们深入幸福的微观和宏观计量经济学吧!
结构:
- 关于数据
- “古典”方法
- 机器学习方法
- 限制
1.关于数据
这里快速回顾一下世界幸福报告数据集的变量。关于数据的更多信息,可以参考我之前的文章这里。
经济指标:
- :按 2011 年不变国际美元购买力平价(PPP)计算的人均 GDP。
- 出生时健康预期寿命 。
幸福指标:
幸福指数主要来自 G allup World Poll (GWP),该公司使用随机选择的、具有全国代表性的样本,持续调查不同国家的居民。这些指标如下:
- 坎特里尔量表 ,或 人生阶梯 :它由要求受访者用 0 到 10 的“阶梯”量表给自己的生活打分组成,其中 0 表示可能最坏的生活。
- :它是前一天快乐、欢笑和享受的平均影响指标。
- 负面影响 :它代表前一天对担心、悲伤和愤怒的影响测量的平均值。
社会指标:
社会指标也来自 GWP 的调查。它们包括:
- 论人均 GDP。
- 做出人生选择的自由 : 它代表了对问题“你对自己选择如何生活的自由感到满意还是不满意?”**
- 对腐败的看法:它代表了两个问题的二元答案的全国平均值:“腐败是否在整个政府中普遍存在?”和“企业内部的腐败现象是否普遍?****
2.“经典”方法
2.1.变量的选择
我必须解决的第一个问题是:为幸福建模保留什么变量?换句话说,在解释幸福时,哪些变量真正重要?
为此,我进行了一次主成分分析 (PCA)。该方法通过构建数量较少的代表性变量,从数据中提取最重要的信息,这些变量共同解释了原始数据集中的大部分可变性。**
称为 P 主分量的新变量是特征空间中的方向,原始数据沿着该方向高度可变。因此,通过分析它们的组成,可以确定为研究保留的变量。
关于 PCA 的更多信息,可以阅读这篇文章。
下图显示了每个主成分解释的差异百分比。前两个解释了大部分的差异。
图 1 —由主成分解释的差异
下图分析了主要成分的构成及其与原始变量的相关性。
图 2——变量对主成分的贡献
图 3 —变量和主成分的相关矩阵
这些图表强烈地表明,有 6 个关键变量需要保留在模型中:人均国内生产总值、社会支持、出生时的健康预期寿命、做出生活选择的自由、慷慨程度和对腐败的看法。综合来看,这六个变量解释了各国之间国家年度平均阶梯得分的大部分差异。**
此外,可以看到每个变量对双标图中两个第一主成分的影响有多强烈。该图还通过代表向量之间的角度显示了变量之间的相关性:角度越小,变量之间的正相关程度越高。**
因此,生命阶梯、人均 GDP、出生时健康预期寿命、和社会支持度解释了第一主成分的大部分,并且相互之间强相关。****
图 4 — PCA 双标图
2.2.幸福建模
相关性有助于快速了解数据。但是,当涉及到识别两个或多个变量之间的相关性是否代表因果关系时,事情就变得棘手了。
我们举一个著名的例子。2012 年,梅瑟利发表了一篇论文,指出一个国家的巧克力消费水平与其人口的认知功能高度相关。这是否意味着吃更多的巧克力会让你变得更苗条?可惜,很可能不是!
关于因果关系的更多信息,你可以阅读这篇文章或者观看下面的视频。
然而,建立因果关系在决策中至关重要。例如,平均而言,出生时健康预期寿命高的国家往往更幸福。这是否意味着确保良好的医疗体系会增加人们的幸福感?视情况而定,因为健康的人仍然可以糟糕地评价他们的生活。
因此,要回答这类问题,就必须使用统计工具,比如回归分析。经济学家经常使用回归分析。它们使他们不仅能够量化一个变量和其他变量之间的关系,而且能够通过统计测试量化其相关性关系。
方法学
在进行回归分析时,一方面,控制在不同国家间保持不变但随时间变化的变量是很重要的。例如,经济危机,如 2008 年的次贷危机,对全球人民的幸福产生了影响。
另一方面,必须控制非时变的未观察到的个体特征,例如乡村天气。这些控制使我们能够真正捕捉到解释变量和幸福之间的因果关系。
为此,我依赖于众所周知的技术固定效果。更具体地说,我根据回归中包含的固定效果考虑了 3 设置。
- 年份效应
- 年份和区域影响
- 年份和国家效应
这种方法类似于世界幸福报告中的方法,除了我通过最小-最大标准化重新调整了变量。这解释了结果的差异。**
结果
不出所料,在这三种情况下,所有变量都很重要。更准确地说:
- **对数人均国内生产总值始终是最重要的因素,其系数值从 0.29 到 0.99 不等。
- **社会支持在第一个情景中排在第一位,在另外两个情景中排在第二位,系数值在 0.21 到 0.29 之间。
- **出生时的健康预期寿命仅在考虑第一种情况下的方差时与 0 有显著差异。在第三种情况下,它的符号甚至是负数。这表明第二种情况的结果是最可靠的。
- **慷慨和对腐败的感知相对不那么重要,它们的方差很高。
3.机器学习方法
在过去的十年里,机器学习领域取得了重大成功。它已被应用于许多领域,并被证明是非常有效的,往往优于通常的统计方法。**
然而,研究首先集中于开发在预测或分类的准确性方面最有效的算法。只是在最近几年才转向需要建立因果干扰。如今,它是数据科学家社区热情的源泉。
在这种背景下,我决定应用在机器学习中使用的最先进的解释技术来更好地理解快乐的驱动因素。**
方法学
为此,我按 4 步进行:
a .国民平均幸福感的建模:我在数据集上测试了不同的机器学习算法,并微调了它们的超参数。
b .变量相对重要性的比较:我用最佳模型计算了每个变量的相对重要性。
c .变量对幸福感的边际影响分析:通过分析解释变量对幸福感的部分依赖性,更进一步。 d .按地区进行的幸福动态分析:通过仔细查看地区层面来细化分析。**
解释变量包括 6 个关键指标(人均国内生产总值、社会支持、出生时健康预期寿命、做出生活选择的自由、慷慨程度和对腐败的看法)作为以及年份和地区固定效应。如前所述,这些变量通过最大-最小标准化进行重新调整。**
我使用了以下 Python 库: scikit-learn 和 skater 。
结果
a .国民平均幸福感的建模
我应用最常用的机器学习算法,根据解释变量预测人生阶梯。为了比较它们在预测方面的表现,我进行了 5 重交叉验证,并比较了平均值均方根误差(RMSE) 。****
下图所示的结果表明,基于树的方法表现出更好的结果。更特别的是,额外的树算法呈现出最低的 RMSE 。**
图 5—使用不同机器学习算法的 5 重交叉验证的平均 RSME
b .变量相对重要性的比较
Extra-Trees 是一种众所周知的算法,它训练多个随机决策树,并通过平均它们的结果来组合它们。该图简要说明了该算法的基本原理和步骤。
图 6 —随机树林, 来源
关于随机树森林的更多信息在 sckit-learn 文档中提供,此处。
额外的树模型,*作为一个基于树的模型,通过特性提供了对其结果的解释。这些被计算为特征存在的每个节点的杂质的减少,通过到达该节点的概率进行加权。*****
下图显示了模型给出的变量的特征重要性。对数人均国内生产总值、社会支持和出生时健康预期寿命似乎是寿命阶梯的最具决定性的因素。其他模型就不是这样了,即随机森林、AdaBoost 和*梯度提升。这些算法将出生时的健康预期寿命放在人均国内生产总值和社会支持的前面。*****
图 7——由额外树模型给出的特征重要性**
c .分析变量对幸福的边际影响
还可以通过绘制部分相关性来可视化这 3 个变量对模型预测的边际影响。下图显示了略微上升的趋势。这意味着对数人均 GDP、社会支持和出生时健康预期寿命中一个变量的增加,而模型中的其他变量保持不变,会导致幸福感的增加。****
图 8-三个最重要变量的部分相关性
通过可视化两个变量对模型预测的边际影响,可以进一步进行分析。下面的 3D 图与我们的预期一致:一个人的财富和社会支持越高,他就越幸福!
图 9-对数人均国内生产总值和社会支持的部分相关性
总的来说,到目前为止进行的分析不仅证实了我们通过操纵数据所看到的洞察力,而且使我们能够量化它们。金钱和健康以及我们所爱的人的社会支持是幸福的支柱。它们是促使人们积极评价自己生活的因素。
然而,这些观察是在世界范围内进行的。我们仍然需要从一个国家的角度探索幸福的动力,以获得新的发现。
d.按地区分析幸福度动态
如果按地理区域考虑国家一级,这种分析就变得更加有趣。特别是,发达国家、新兴国家和发展中国家之间存在着巨大的差距。这些国家对事物的评价不同。我们来看看吧!
北美
北美国家的结果最令人惊讶,与其他国家截然不同:对数人均 GDP 落后于其他因素。它的重要性仅仅代表了做出生活选择的自由的一半。出生时的健康预期寿命、社会支持、和对腐败的看法排在之后,在幸福的模型预测中具有相对相似的重要性。**
图 10 —北美和 ANZ 国家模型给出的特征重要性
**对数人均国内生产总值、社会支持和出生时健康预期寿命的偏相关图显示出趋于平缓的增长趋势。这表明,在某一点之后,他们价值的增加并不会导致幸福的显著增加。
图 11 —部分依赖
对于西欧国家来说,对数人均国内生产总值和对腐败的看法在决定人们的幸福感方面扮演着相似的角色。同样,做出生活选择的自由排在第三位,紧接在社会支持之前。相反,出生时健康预期寿命落后于其他变量。**
图 12 —西欧国家模型给出的特征重要性
至于北美国家, Log 人均国内生产总值的部分依赖图显示出越来越趋于平缓的趋势,这表明在某个点之后,财富的增加不会导致幸福的显著增加。
对于新兴和发展中国家来说,对数人均 GDP始终是最重要的因素。接下来的变量取决于地区,通常是社会支持或*出生时的健康预期寿命。在南亚国家,社会支持几乎和人均 GDP 一样重要。*****
图 13 —南亚国家模型给出的特征重要性
更令人惊讶的是对对数人均国内生产总值的部分依赖呈钟形。这意味着,在财富达到一定水平后,一个人财富的增加会导致他幸福度的下降!
图 14——三个最重要变量的部分相关性
**社会支持也是中欧和东欧、撒哈拉以南非洲、独立国家联合体国家的第二个最重要的变量,但相对比例并不总是相同,如下图所示。
图 15 —中欧和东欧国家模型给出的特征重要性
图 16——撒哈拉以南非洲国家模型给出的特征重要性
相反,拉丁美洲和加勒比海、中东和北非东部、东亚和东南亚的国家更“重视”出生时健康的预期寿命,而不是社会支持,如下图所示。
图 17—模型对拉丁美洲和加勒比海国家的重要性
图 18 —中东和北非国家模型给出的特征重要性
图 19 —东亚国家模型给出的特征重要性
图 20 —东南亚国家模型给出的特征重要性
如何解释国家之间的这些差异?
有些人可能会说这完全是关于国家的文化和价值观,但实际上这是一个需要更深入研究的棘手问题。尽管如此,所进行的分析已经使我们知道一个国家的人民最重视什么,以及应该采取什么政策来改善他们的生活条件。
限制
最后,我想指出上述方法的 3 个主要局限性:
- 省略变量:虽然直观上与给定的题目相关,但用来解释快乐的变量可能还不够。它们的相关性可能实际上反映了分析中没有包括的一些其他“未观察到的”因素。
- 反向因果关系:还存在双向因果关系的风险。这意味着幸福会引起一个或多个解释变量的变化。例如,人们快乐是因为他们健康,还是他们快乐的情绪让他们感觉健康?人们快乐是因为他们富有,还是快乐的感觉让他们在职业生活中更有活力和雄心?
- 测量错误:许多被分析的变量都是主观的,更多地代表了人们的感知而非现实。例如,来自一个有着悠久民主政治传统的国家的人们可能会更强烈地感受到腐败,而腐败比其他国家更弱、更不普遍。此外,选择人均 GDP和出生时健康预期寿命分别代表人们的财富和健康。****
了解 HDBSCAN 和基于密度的聚类
自上而下全面介绍 HDBSCAN 聚类算法的内部工作原理以及基于密度的聚类的关键概念
HDBSCAN 是由 Campello、Moulavi 和 Sander [8]开发的一种聚类算法。它代表“带噪声的应用的基于层次密度的空间聚类”
在这篇博文中,我将尝试以自上而下的方式介绍一些关键概念,以帮助理解 HDBSCAN 的工作方式和原因。这是为了补充现有的文档,如 sklearn 的“【HDBSCAN 如何工作”[1],以及麦金尼斯和希利的其他作品和演示文稿2,[3]。
除了一些噪音,没有(很少)假设
让我们从最上面开始。在我们描述我们的聚类算法之前,我们应该问,“我们要聚类什么类型的数据?”
我们希望对我们的数据有尽可能少的假设。也许我们唯一可以安全做出的假设是:
- 我们的数据中有噪音
- 在我们的数据中有我们希望发现的聚类
聚类数据集
为了激发我们的讨论,我们从[1]和[3]中使用的数据集开始。
只有二维,我们可以绘制数据,并在我们的数据集中识别 6 个“自然”集群。我们希望通过一些聚类算法来自动识别这些。
k 均值与 HDBSCAN
知道了预期的聚类数,我们运行经典的 K-means 算法,并将结果标签与使用 HDBSCAN 得到的标签进行比较。
即使提供了正确数量的聚类,K-means 也明显无法将数据分组到有用的聚类中。另一方面,HDBSCAN 为我们提供了预期的聚类。
K-means 为什么会失败?
简而言之, K-means 表现不佳,因为不满足对聚类形状的基本假设;这是一个参数算法,由 K 个簇形心、高斯球的中心来参数化。当聚类满足以下条件时,K-means 表现最佳:
- “圆形”或球形
- 大小相等
- 同等密集
- 球体中心密度最大
- 未被噪音/异常值污染
让我们从 ESLR [4]借用一个简单的例子来说明 K-means 如何对簇的形状敏感。下面是来自相同数据的两个聚类。在左边,数据在聚类前被标准化。没有标准化,我们会得到一个“错误的”聚类。
图 14.5 摘自《ESLR》第十四章[4]。标准化数据的聚类(左)与原始数据的聚类(右)。
我们的数据有什么特点?
我们回到我们的原始数据集,通过简单地描述它,很明显为什么 K-means 有困难。该数据集具有:
- 任意形状的簇
- 不同大小的集群
- 不同密度的集群
- 一些噪音和一些异常值
需要稳健的数据探索
虽然每个要点都可以从真实世界的数据集中合理地得到,但每个要点对于参数算法(如 K-means)来说都是有问题的。在信任算法的输出之前,我们可能需要检查算法的假设是否成立。但是,当对数据知之甚少时,检查这些假设可能是困难的。这是不幸的,因为聚类算法的主要用途之一是数据探索,我们仍在理解数据的过程中
因此,将用于数据探索的聚类算法需要尽可能少的假设,以便我们获得的初始洞察是“有用的”;更少的假设使其更加稳健,适用于更广泛的真实世界数据。
稠密区和多元模式
现在,我们知道了我们要处理的是什么类型的数据,让我们来探讨一下 HDBSCAN 的核心思想,以及它在数据具有以下特征时如何表现出色:
- 任意形状的簇
- 不同大小和密度的集群
- 噪音
HDBSCAN 使用基于密度的方法,该方法很少对聚类进行隐含假设。它是一种非参数方法,寻找由基础分布的多元模式形成的聚类层次。它不是寻找具有特定形状的簇,而是寻找比周围空间更密集的数据区域。你可以使用的心理图像是试图将岛屿从海洋中分离出来,或者将山脉从山谷中分离出来。
什么是集群?
我们如何定义“集群”?我们直觉认为的集群的特征可能很难定义,并且通常是特定于上下文的。(概述见克里斯蒂安·亨宁的演讲[5])
如果我们回到原始数据集,我们识别簇的原因是我们看到被稀疏和嘈杂空间包围的 6 个密集区域。
被包围的区域密度很高
一种通常与我们对集群的直观概念一致的定义集群的方式是:由稀疏区域分隔的高密度区域。
请看一维模拟数据的曲线图。我们可以看到 3 个集群。
看看下面的分布
X 是来自混合正态分布的模拟数据,我们可以画出 X 的精确概率分布
峰值=密集区域。波谷=稀疏区域
波峰对应于最密集的区域,波谷对应于稀疏的区域。这给了我们另一种解决问题的方法,假设我们知道潜在的分布,簇是被不可能的区域分开的高度可能的区域。想象更高维的概率分布形成了山脉和山谷的景观,其中山脉是你的集群。
给三座山峰/山脉/集群着色
对于不太熟悉的人来说,这两种说法实际上是一样的:
- 由稀疏区域分隔的高密度区域
- 被不太可能的区域分开的高度可能的区域
一种是通过概率分布来描述数据,另一种是通过分布中的随机样本。
PDF 图和上面的带状图是等效的。PDF,概率密度函数,解释为位于某点周围小区域内的概率,从 X 看样本时,也可以解释为该点周围的期望密度。
给定基础分布,我们预计在随机样本中,更有可能的区域往往会有更多的点(更密集)。同样,给定一个随机样本,你可以根据经验密度推断出一个区域的概率。
随机样本中更密集的区域对应于基础分布中更可能的区域。
事实上,如果我们观察 X 的随机样本的直方图,我们会发现它看起来与 X 的真实分布完全一样。直方图有时被称为经验概率分布,有了足够的数据,我们希望直方图收敛到真实的基本分布。
还是那句话,密度=概率。密度越大=可能性越大。
但是……什么是集群?
可悲的是,即使有了我们对集群的定义,也很难知道某个东西是否是一个单独的集群。看看下面的例子,我们把 X 的一个模式向右移动了。虽然我们还有 3 个峰值,但是我们有 3 个集群吗?在某些情况下,我们可以考虑 3 个集群。“直觉上”我们说只有两个集群。我们如何决定?
通过观察X’,的带状图,我们可以更加确定只有两个星团。
X 有 3 个簇,X’有 2 个簇。集群的数量在什么时候会发生变化?
定义它的一种方法是为底层分布的 PDF 设置一些全局阈值。所得水平集的连通分量就是你的聚类[3]。这就是 DBSCAN 算法所做的,在多个层次上做将导致 DeBaCl [7]。
基于两个不同水平集的两个不同聚类
这可能因为它的简单而吸引人,但是不要被愚弄了!我们最终得到一个额外的超参数,阈值𝜆,,我们可能需要对其进行微调。此外,这对于具有不同密度的集群来说不太适用。
为了帮助我们选择,我们给我们的聚类选择涂上颜色,如下图所示。我们应该只考虑蓝色和黄色、或绿色吗?
左侧有 3 个集群,右侧有 2 个集群
要选择,我们看哪一个更“坚持”。我们看到他们在一起多还是分开多?我们可以用彩色区域的面积来量化。
在左侧,我们看到蓝色区域和黄色区域和黄色区域的面积之和大于绿色区域和绿色区域的面积之和。这意味着两个峰值更加突出,因此我们决定它们是两个独立的集群。
在右边,我们看到绿色的面积要大得多。这意味着它们只是“凸起”而不是峰值。所以我们说它们只是一个集群。
在文献2中,这些区域的面积是持续时间、的度量,这种方法称为eom
或质量过剩。更正式的说法是,我们在所选聚类不重叠的约束下,最大化聚类的持久性总和。
构建层次结构
通过在 𝜆 的不同值处得到多个水平集,我们得到一个层次结构。对于多维设置,想象星团是海洋中间的岛屿。随着你降低海平面,岛屿将开始“生长”,最终岛屿将开始彼此连接。
为了能够捕捉和表示集群(岛)之间的这些关系,我们将其表示为一个层次树。这种表示推广到更高的维度,是一种自然的抽象,更容易表示为我们可以遍历和操作的数据结构。
将集群层次结构可视化为树
按照惯例,树是自上而下绘制的,其中根(所有东西都是一个集群的节点)在顶部,树向下生长。
自上而下可视化树
如果您正在使用 HDBSCAN 库,您可能会使用clusterer.condensed_tree_.plot()
API。下面显示的结果与上面显示的结果相同。圈出的节点对应于所选择的簇,分别是黄色、蓝色和红色区域。
来自 HDBSCAN 的压缩树图
使用 HDBSCAN 时,这个特定的图可能有助于评估集群的质量,并有助于微调超参数,我们将在“参数选择”部分讨论。
局部近似密度
在上一节中,我们访问了底层发行版的真实 PDF。然而,对于真实世界的数据,基本分布几乎总是未知的。
因此,我们必须使用经验密度来估计 PDF。我们已经讨论了一种方法,使用直方图。然而,这只对一维数据有用,并且随着维数的增加,计算变得困难。
我们需要其他方法来得到经验概率密度函数。这里有两种方法:
- 计算𝜀-radius 内特定点的邻居数量
- 寻找到第 K 个最近邻居的距离(这是 HDBSCAN 使用的)
计算𝜀-radius 境内的邻居
对于每个点,我们围绕该点画一个𝜀-radius 超球,并计算其中的点数。这是我们对空间中该点密度的局部近似。
使用邻居计数估计概率密度函数
我们对每个点都这样做,并将估计的 PDF 与 PDF 的真实值进行比较(我们现在才这样做,因为我们模拟了数据,它的分布是我们定义的)。
对于我们的一维模拟数据,邻居计数与 PDF 的真实值高度相关。邻居的数量越多,估计的 PDF 就越高。
使用邻居计数 eps = 0.1 估计 X 的 PDF
我们看到,这种方法可以很好地估计模拟数据 x 的 PDF。请注意,这可能对数据规模和样本大小很敏感。您可能需要迭代𝜀的几个值才能获得好的结果。
到第 K 个最近邻居的距离
在这个例子中,我们得到了前面方法的补充。我们不是设置𝜀然后计算邻居,而是确定我们想要的邻居数量,并找到包含这 k 个邻居的𝜀的最小值。
K = 7 时的核心距离
结果就是我们在 HDBSCAN 中所说的核心距离。核心距离较小的点位于密度较大的区域,因此 PDF 的估计值较高。具有更大核心距离的点位于更稀疏的区域,因为我们必须行进更大的距离来包含足够多的邻居。
使用核心距离估算 X 的 PDF,其中 K = 100
我们尝试在模拟数据 x 上估计 PDF。在上面的图中,我们使用1/core_distance
作为 PDF 的估计值。正如所料,估计值与真实的 PDF 高度相关。
虽然以前的方法对数据的规模和数据集的大小都很敏感,但这种方法主要对数据集的大小敏感。如果您均等地缩放每个维度,那么所有核心距离将成比例地增加。
这里的关键要点是:
- 核心距离=密度估计值
- (回想一下)密度=概率
- 核心距离 PDF 的一些估计值
所以当我们提到一个点的核心距离时,你可以想到隐含地提到 PDF。基于核距离过滤点类似于从底层分布获得水平集。
每当我们有core_distance ≤ 𝜀
,就有一个隐含的pdf(x) ≥ 𝜆
在发生。𝜀和𝜆之间总是有一个映射,为了简单起见,我们将只使用符号𝜆来表示核心距离和 PDF。
找到水平集并给区域着色
回想一下,在前面的例子中,我们从 PDF 中得到一个水平集,得到的区域就是我们的聚类。这很容易,因为一个区域被表示为某种形状。但是当我们处理点的时候,我们怎么知道不同的区域是什么呢?
左边是一个小数据集,右边是相应的 PDF。
PDF 不“准确”
第一步是找到某个𝜆的水平集。我们用core_distance ≤ 𝜆
过滤区域pdf(x) ≥ 𝜆
或者过滤点。
现在我们需要找到不同的区域。这是通过将“附近”的点相互连接来实现的。“附近”是由𝜆定义的当前密度水平决定的,如果两个点的欧几里德距离小于𝜆.,我们说这两个点足够近
我们围绕每个点画一个半径为𝜆的球体。
我们将该点连接到其𝜆-球内的所有点。如果两个点相连,则它们属于同一区域,并且应该具有相同的颜色。
对每个点都这样做,我们剩下的是几个相连的部分。这些是我们的集群。
这是你在某个水平集上得到的聚类。我们继续“降低海平面”,并跟踪新的星团出现,一些星团增长,最终一些合并在一起。
降低海平面
这里有四个可视化,我们在 4 个不同的水平集显示 4 个集群。我们跟踪不同的集群,以便能够构建我们之前讨论过的层次树。
定义新的距离度量
我想强调的是,点可以在𝜆-球内,但它们仍然不会连接。它们必须首先包含在水平集内,因此对于要考虑的点来说, 𝜆 应该大于其核心距离。
两点最终连接的 𝜆 的值可以解释为某个新的距离。对于要连接的两点,它们必须是:
- 在足够密集的区域
- 彼此足够接近
对于 a 和 b ,我们根据 𝜆 得到如下不等式:
- 核心 _ 距离(a) ≤ 𝜆
- 核心 _ 距离(b) ≤ 𝜆
- 距离(a,b) ≤ 𝜆
(1)和(2)是针对“在足够密集的区域”。③是为【彼此足够接近】
结合这些不等式,能够直接连接 a 和 b 所需的𝜆的最小值为
mutual_reachability_distance(a, b) *=* max(
core_distance(a),
core_distance(b),
distance(a, b)
)
这在 HDBSCAN 文献中称为相互可达性距离。
投影到 𝜆-space
注意:这个“λ空间”是一个在文献中找不到的术语。这只是为了这个博客。
我们现在可以使用相互可达性距离作为新的度量,而不是使用欧几里德距离作为我们的度量。使用它作为度量相当于将点嵌入一些新的度量空间,我们将简单地称之为 𝜆-space.*
排斥效应。圆圈代表每个点的核心距离。
这具有在稀疏区域中分散接近点的效果。
由于随机样本的随机性,两个点可以在非常稀疏的区域中彼此接近。然而,我们期望稀疏区域中的点彼此相距很远。通过使用相互可达性距离,稀疏区域中的点如果过于靠近就会“排斥其他点”,而非常密集区域中的点则不受影响。
下面是使用多维缩放在𝜆-space 投影的点的图,以更具体地显示其效果。
我们可以在左边和上面看到这种排斥效应。左边的四个点是最分散的,因为它们在一个非常稀疏的空间中。
使用𝜆-space 构建层次结构树
回想一下,为了构建层次结构树,我们有以下步骤:
- 将 𝜆 设置为核心距离的最小值
- 过滤水平集中的点
- 连接最多相距 𝜆 单位的点
- 创建新集群、扩展新集群和合并集群
- 将 𝜆 设置为核心距离的下一个最小值,并转到步骤(2)
注意,在执行步骤(3)时,连接已经属于同一个连通分量的两个点是没有用的。真正重要的是集群之间的联系。将连接两个聚类的连接对应于来自两个不同聚类的具有最小相互可达性距离的点对。如果我们忽略这些“无用”的连接,只注意相关的连接,我们剩下的是一个有序的边列表,这些边总是合并两个簇(连接的组件)。
丢弃“无用”边的连接…这是最小生成树的形成吗?
这听起来可能很复杂,但如果我们将相互可达性距离视为我们的新指标 : ,这就可以简化
- 将点嵌入𝜆-space,并将每个点视为一个独立的聚类
- 找出两个不同聚类中两点之间的最短距离
- 合并两个集群
- 回到步骤(2 ),直到只有一个集群
如果这听起来很熟悉,这就是经典的凝聚聚类。这只是𝜆-space 的单一连锁集群!
在欧几里得空间中进行单链聚类可能对噪声敏感,因为噪声点可能会形成跨越岛的伪桥。通过在𝜆-space 中嵌入点,“排斥效应”使得聚类对噪声更加鲁棒。
单一链接集群相当于构建一棵最小生成树!因此,我们可以利用图论中所有有效的方法来构造 MST。
HDBSCAN 的最小生成树
参数选择和其他注意事项
现在,我们来了解一下关于 HDBSCAN、min_samples
和min_cluster_size
以及 HDBSCAN 的主要参数的说明。
最小样本数
回想一下我们的模拟数据 X,在这里我们试图估计真实的 PDF。
我们尝试使用核心距离来估计这一点,核心距离是到第 K 个最近邻居的距离。超参数 K 在 HDBSCAN API 中被称为min_samples
。
这些只是来自模拟数据的经验观察。我们将上面的图与基于不同min_samples
值的估计 PDF 进行比较。
基于 10000 样本量的估计 PDF
正如你所看到的,设置min_samples
太低会导致 PDF 非常嘈杂的估计,因为核心距离变得对密度的局部变化敏感。这可能导致虚假集群,或者一些大集群可能最终分裂成许多小集群。
设置min_samples
过高会使 PDF 过于平滑。PDF 的更精细的细节丢失了,但是至少你能够捕捉到更大更全局的底层分布结构。在上面的例子中,两个小集群被“模糊”成一个集群。
确定min_samples
的最佳值可能很困难,并且最终取决于数据。不要被我们这里使用的min_samples
的高值所误导。我们使用 1-d 模拟数据,该数据在整个域和仅 3 个集群中具有平滑的密度变化。典型的真实世界数据是完全不同的特性,较小的min_samples
值就足够了。
关于平滑效果的见解肯定适用于其他数据集。增加min_samples
的值可以平滑估计的分布,从而使小峰变平,我们可以只关注更密集的区域。
对于
min_samples
所做的事情,最简单的直觉是提供一个你希望你的聚类有多保守的度量。您提供的min_samples
值越大,聚类就越保守——更多的点将被声明为噪声,聚类将被限制在越来越密集的区域。[7]
请小心,这样做的一个可能的副作用是,它可能需要更长的运行时间,因为您必须为每个点找到更多的“最近邻居”,并且可能需要更多的内存。
最小聚类大小
请注意,我们试图估计的潜在 PDF 非常平滑,但因为我们试图用样本进行估计,所以我们预计估计值会有一些变化。
这导致一个“颠簸”的估计 PDF。让我们关注 PDF 的一小部分来说明这一点。
这种凹凸在层次树中有什么影响?这影响了集群的持久性度量。
因为小突起被解释为迷你聚类,所以真实聚类的持久性度量被划分为小段。如果不移除凸起,通过质量过剩方法可能看不到主星团。它看到的不是一座光滑的大山,而是无数迷你山峰的集合。
为了解决这个问题,我们把这些小突起弄平。这是通过“修剪”层次树中不够大的集群来实现的。这样做的效果是质量过剩方法不再被小突起分散注意力,现在可以看到主星团。
min_cluster_size
规定了在被认为是峰之前“凸起”的最大尺寸。通过增加min_cluster_size
的值,你在某种程度上平滑了估计的 PDF,使得分布的真正峰值变得突出。
因为我们可以访问 X 的真实 PDF,我们知道一个好的min_samples
值,它将产生一个平滑的估计 PDF。如果估计不错,那么min_cluster_size
就不那么重要了。
理想凝聚树
假设我们对min_samples
使用了一个较小的值,并将其设置为 100。如果你看 PDF 图,它有 PDF 的一般形状,但有明显的差异。
尽管我们知道应该只有 3 个峰值,但我们看到了许多小峰。
如果你看到一个更极端的版本,也许你甚至看不到条形的颜色了,那么这意味着层次结构树是复杂的。也许是因为估计的方差,也许这就是数据的结构。解决这个问题的一个方法是增加min_cluster_size
,这有助于 hdb 简化树,专注于更大更全局的结构。
数据转换
尽管我们已经确定 HDBSCAN 可以找到任意形状的簇,但这并不意味着不需要任何数据转换。这真的取决于你的用例。
缩放某些特征可以增加或减少该特征的影响。此外,一些变换如对数和平方根变换可以完全改变底层分布的形状。
评估集群质量
另一个需要注意的观点是,在使用 HDSCAN 时,评估和总结集群的传统方法可能没有意义。当聚类为圆形时,一些度量标准(如轮廓得分)工作得最好。
对于 sklearn 中的“月亮”数据集,K-means 比 HDBSCAN 的结果具有更好的轮廓得分,即使我们看到 HDBSCAN 中的聚类更好。
这也适用于通过获取聚类所有点的平均值来汇总聚类。这对于 K-means 非常有用,是一个很好的集群原型。但是对于 HDBSCAN 来说,可能会有问题,因为星团不是圆的。
空心圆是星团的“质心”。
均值点可以远离实际集群!这可能会非常误导人,并导致错误的见解。您可能想要使用类似于 medoid 的东西,它是最接近所有其他点的集群的一部分。但是要小心,试图用空间中的一个点来概括一个复杂的形状可能会丢失太多的信息。
这完全取决于您喜欢哪种类型的集群以及您正在处理的底层数据。请参见 Henning's talk [5] 了解集群评估的概述。
HDBSCAN 摘要
我们完了!我们已经讨论了 HDBSCAN 的核心思想!我们将简要介绍一些具体的实现细节。
HDBSCAN 的实施大致如下:
- 计算每个点的核心距离
- 使用
mutual_reachability(a, b)
作为每个 a、b 的距离度量 - 构建一棵最小生成树
- 修剪这棵树
- 使用过剩质量选择集群
计算每个点的核心距离
这基本上是我们“估计潜在 pdf”的方式
使用相互可达性距离的最小生成树
相互可达距离是在什么层次的 𝜆 两个在一起的点将连接的总结。这就是我们使用的新度量标准。
构建最小生成树相当于𝜆-space 中的单链接聚类,这相当于遍历每个可能的水平集并跟踪聚类。
修剪生成的树
简而言之,因为我们得到的只是一个估计的 PDF,我们期望有一些差异。因此,即使底层分布非常平滑,估计的 PDF 也可能非常不平坦,因此导致非常复杂的层次树。
我们使用参数min_cluster_size
来平滑估计分布的曲线,因此,将树简化为condensed_tree_
用“质量过剩”来选择星团
使用浓缩树,我们可以估计每个集群的持久性,然后计算出最佳集群,如前一节所述。
参考
[1]https://hdb scan . readthedocs . io/en/latest/how _ hdb scan _ works . html
2麦金尼斯,利兰和约翰希利。加速分层密度聚类。 arXiv 预印本 arXiv:1705.07321 (2017)。
[3]约翰·希利。 HDBSCAN,基于快速密度的聚类,方法和原因。 PyData 纽约。2018
[4]哈斯蒂、特雷弗、罗伯特·蒂布拉尼和杰罗姆·弗里德曼。统计学习的要素:数据挖掘、推理和预测。斯普林格科学&商业媒体,2009 年。
[5]克里斯蒂安·亨宁。评估聚类质量。PyData 纽约。2018.
[6]亚历山德罗·里纳尔多。 DeBaCl:一种基于密度的聚类算法及其性质。
[7]https://hdb scan . readthedocs . io/en/latest/parameter _ selection . html
[8]坎佩洛、里卡多·JGB、达武德·穆拉维和约尔格·桑德。"基于层次密度估计的密度聚类."亚太知识发现和数据挖掘会议。施普林格,柏林,海德堡,2013。
照片由【Dan Otis】【on】【不连续】 , 【泰 51】【凯西荷纳】 【对 【非致命】 , 】
了解 Oracle 中的层次结构
这篇文章讲述了在 Oracle 数据库中处理分层数据。它通过示例给出了详细的描述,从概念上解释了层次查询,并构建了层次查询以满足业务需求。
由Edvard Alexander lvaag在 Unsplash 上拍摄的照片
树形结构
在现实生活中,有很多情况下,一组特定的数据依赖于另一组数据,而另一组数据又依赖于另一组数据,如此等等。因此,这种数据的逻辑结构形成了树形结构。关系数据库不以分层的方式存储数据。因此,通常很难在关系数据库中浏览这些数据。Oracle 提供了许多分层查询特性,使我们能够处理这样的数据。
从连接方式开始
Oracle 的“START WITH CONNECT BY”子句允许非常有效地遍历分层数据。为了理解它的工作原理,我们来看一个示例表,其中包含如下示例数据:
CREATE TABLE ENTITIES(PARENT_ENTITY VARCHAR2(20 BYTE),CHILD_ENTITY VARCHAR2(20 BYTE),VAL Number);
现在在其中插入一些示例值。
Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (NULL,’a’,100);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘a’, ‘af’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘a’, ‘ab’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘a’, ‘ax’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘ab’, ‘abc’,10);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘ab’, ‘abd’,10);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘ab’, ‘abe’,10);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘abe’, ‘abes’,1); Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘abe’, ‘abet’,1); Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (NULL,’b’,100);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘b’, ‘bg’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘b’, ‘bh’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘b’, ‘bi’,50);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘bi’, ‘biq’,10);Insert into ENTITIES (PARENT_ENTITY, CHILD_ENTITY,VAL) Values (‘bi’, ‘biv’,10);COMMIT;
数据层次结构如下所示:
要遍历此类数据,Oracle 中的 SQL 查询可以写成:
SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL CONNECT BY PRIOR child_entity= parent_entity
现在,让我们分析一下这个查询。
开始于:指定层次的根行,即从哪里开始“行走”。
CONNECT BY: 指定层次结构中父行和子行之间的关系。
先验:一元运算符,用于实现递归条件,即实际行走。
上述查询的结果如下:
执行过程如下:
a.>开始条件决定了开始点。在我们的例子中,我们将开始条件指定为 parent_entity 为 NULL。因此,它将标记 parent_entity 为 NULL 的那些行。
b.>条件 CONNECT BY PRIOR child _ entity = parent _ entity 将导致子实体值成为父实体,直到遍历整个分支。因此,在我们的例子中,值“a”将成为父值,它将搜索其子值。返回的第一行将是:
a 的孩子是 ab,af,ax。它现在将 ab 作为父节点并搜索其子节点。因此,到目前为止的结果集将是:
' ab '的孩子是 abc 和 abd 和 abe。它现在将搜索 abc 的子节点。因为
“abc”没有任何子级,它将在结果集中返回它,并将搜索 abd 的子级。它也没有任何子元素,所以它也将在结果集中返回这个。到目前为止的结果集是:
它现在会寻找“亚伯”的孩子。它有两个孩子:abes 和 abet。它现在将搜索 abes 的子节点,然后搜索 abet。abes 和 abet 都没有孩子。因此,结果集将如下所示:
它现在将搜索 af 的孩子。它没有任何子元素,因此将在结果集中返回。同样,ax 将在结果集中返回。
这就完成了对步骤 a 中返回的第一行的遍历。现在将对步骤 1 中返回的第二行重复相同的过程。最终结果集将如下所示:
遍历方向
层次结构可以双向遍历:自顶向下或自底向上。CONNECT BY 子句中的条件和运算符“PRIOR”的位置决定了遍历的方向。
下面的查询以自上而下的方向遍历行。
**SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity= parent_entity**
如果我们将 PRIOR 放在条件的右侧,它将自下而上遍历行:
SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY child_entity= PRIOR parent_entity
该查询的输出将是:
这将尝试在 START WITH 条件中找出返回行的父行。由于上述行没有父记录,因此这是返回的结果集。如果您将上述查询更改如下:
**SELECT parent_entity,child_entity
FROM entities
START WITH child_entity =’abet’
CONNECT BY child_entity= PRIOR parent_entity**
结果集将是
因此,它从 child_entity='abet '处开始自下而上地遍历数据
通过改变列 child_entity 和 parent_entity 的位置,同时保持在左侧之前,可以获得相同的结果。因此,以下查询也将产生上述结果集:
**SELECT parent_entity,child_entity
FROM entities
START WITH child_entity =’abet’
CONNECT BY PRIOR parent_entity = child_entity**
水平
LEVEL 是一个 Oracle 伪列,显示层次结构树中特定行的级别或等级。只有当查询中存在 CONNECT BY 子句时,才能使用它。如果我们执行下面的查询:
SELECT level, parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity= parent_entity
结果将如下所示:
使用函数 LPAD 和级别,我们可以在结果集中返回一个树状结构,如下所示:
SELECT LPAD(child_entity,Length(child_entity) + LEVEL * 10–10,’-’) tree
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity= parent_entity
修剪树枝
可能存在部分检索分层树和修剪分支的业务需求。比方说,在我们的数据中,我们不需要以' ab '开头的分支和以下分支。
为此,我们将把查询修改为:
**SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity = parent_entity and child_entity !=’ab’**
结果将如下所示:
NOCYCLE 和 CONNECT_BY_ISCYCLE
如果分层数据包含循环,即如果当前行有一个也是其祖先的子行,则可能会出现这种情况。例如,在实体表上执行下面的 Insert 语句:
**insert into entities values(‘abet’,’a’);**
它添加了一行,其中“abet”是“a”的父级。但是,‘a’也是‘abet’的祖先。这将导致一个循环。如果您执行以下查询:
它添加了一行,其中“abet”是“a”的父级。但是,‘a’也是‘abet’的祖先。这将导致一个循环。如果您执行以下查询:
**SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity = parent_entity**
然后,会弹出以下错误:
若要在数据中存在 CONNECT BY 循环的情况下从查询中返回行,请按如下方式使用 NOCYCLE 参数:
**SELECT parent_entity,child_entity
FROM entities
START WITH parent_entity is NULL
CONNECT BY NOCYCLE PRIOR child_entity = parent_entity**
这不会返回错误。如果我们需要知道循环存在于哪一行,我们可以使用伪列 CONNECT_BY_ISCYCLE 。如果当前行有一个也是其祖先的子行,则返回 1。否则,它返回 0。因此,如果您运行以下查询:
**SELECT parent_entity,child_entity,CONNECT_BY_ISCYCLE isloop
FROM entities
START WITH parent_entity is NULL
CONNECT BY NOCYCLE PRIOR child_entity = parent_entity**
输出将是:
系统连接路径
如果要查看从根节点到当前节点的完整路径,可以按如下方式使用 SYS_CONNECT_BY_PATH:
**SELECT parent_entity,child_entity,SYS_CONNECT_BY_PATH(child_entity,’\’) PATH
FROM entities
START WITH parent_entity is NULL
CONNECT BY PRIOR child_entity = parent_entity**
输出将是:
对于每一行,它显示了从顶层节点到当前节点的完整路径。
在从数据库中获取信息时,遍历层次结构通常会带来挑战。使用 Oracle 的内置功能可以解决这个问题。希望这篇文章对如何做到这一点有所启发。快乐阅读!
用典型相关分析理解学校如何工作
Python 典型相关分析简介
彼得·巴克斯在 Unsplash 上的照片
美国数据科学家把大部分时间花在分析数据中的关系和模式上。然而,我们的大多数探索工具都集中在一对一的关系上。但是,如果我们想有一个更概括的观点,并找到某些变量组之间的共性和模式呢?
这篇文章包括:
- 典型相关分析的介绍,让我们一次确定变量组之间的关联。
- 一个关于学校环境如何影响学生表现的 Python CCA 教程。
那么什么是 CCA 呢?
假设我们想知道学校的氛围如何影响学生的学业成就。一方面,在他们的学习环境中,我们有关于支持、信任和协作水平的变量。另一方面,我们有学生的学习成绩和考试成绩。
CCA 让我们从整体上探索这两组变量之间的关联,而不是在单个基础上考虑它们。粗略地说,我们为这些变量集合中的每一个集合提出了一个集合表示(一个潜在变量,称为规范变量),以最大化这些变量之间的相关性。
首先,为什么 CCA 有用?
在深入研究一堆方程之前,让我们看看为什么它值得努力。
借助 CCA,我们可以:
- 找出两组变量是否独立,或者,如果有一组变量,测量它们之间的关系。
- 通过评估每个变量对规范变量(即组件)的贡献来解释它们之间关系的本质,并找出两个集合之间的共同维度。
- 将关系总结成较少的统计数据。
- 进行降维,将某些变量组的存在考虑在内。
构造标准变量
给定两组变量:
我们将第一对规范变量构建为每组变量的线性组合:
其中权重(a1,… ap),(b1,…,bq)以两个变量之间的相关性最大化的方式选择。
计算第一标准变量。
我们有一对协变量:
典型相关系数是典型变量 CVX 和 CVY 之间的相关性。
为了计算第二对协变量,我们通过添加另一个约束来执行相同的过程:每个新变量都应该与前一个变量正交且不相关。
计算第二对变量。
我们以类似的方式计算 min(p,q)对,并最终得到准备探索的 min(p,q)分量。(注意,每组中变量的数量不必相同。)
就像在 PCA 中一样,我们将数据投射到最小(p,q)潜在维度上。然而,并不是所有的都是信息丰富和重要的。让我们看看下面例子中规范变量的构成和解释。
纽约市学校数据
我们将使用来自纽约学校数据集的两个变量组:
第 1 组:环境指标
- 严格指导%
- 协作教师%
- 支持性环境%
- 有效的学校领导%
- 家庭-社区关系%
- 信任百分比
第 2 组:绩效指标
- 平均 ELA 熟练程度
- 平均数学水平
至于工具,我们将使用 pyrcca 实现。
我们从在单个数据帧中分离每个变量组开始:
import pandas as pd
import numpy as np
df = pd.read_csv('2016 School Explorer.csv')# choose relevant features
df = df[['Rigorous Instruction %',
'Collaborative Teachers %',
'Supportive Environment %',
'Effective School Leadership %',
'Strong Family-Community Ties %',
'Trust %','Average ELA Proficiency',
'Average Math Proficiency']]# drop missing values
df = df.dropna()# separate X and Y groupsX = df[['Rigorous Instruction %',
'Collaborative Teachers %',
'Supportive Environment %',
'Effective School Leadership %',
'Strong Family-Community Ties %',
'Trust %'
]]Y = df[['Average ELA Proficiency',
'Average Math Proficiency']]
X 组
Y 组
将 X 组转换成数字变量,并将数据标准化:
for col in X.columns:
X[col] = X[col].str.strip('%')
X[col] = X[col].astype('int')# Standardise the datafrom sklearn.preprocessing import StandardScaler
sc = StandardScaler(with_mean=True, with_std=True)
X_sc = sc.fit_transform(X)
Y_sc = sc.fit_transform(Y)
在相关的预处理之后,我们准备应用 CCA。请注意,我们将正则化参数设置为 0,因为正则化 CCA 超出了本文的范围(不过我们将在未来的文章中回到这个问题)。
import pyrcca
nComponents = 2 # min(p,q) components
cca = pyrcca.CCA(kernelcca = False, reg = 0., numCC = nComponents,)
# train on data
cca.train([X_sc, Y_sc])
print('Canonical Correlation Per Component Pair:',cca.cancorrs)
print('% Shared Variance:',cca.cancorrs**2)
数字背后的含义
临床相关性
>> Canonical Correlation Per Component Pair: [0.46059902 0.18447786]
>> % Shared Variance: [0.21215146 0.03403208]
对于我们的两对正则变量,我们的正则相关分别为 0.46 和 0.18。因此,学校氛围和学生表现的潜在表征确实具有 0.46 的正相关,并且共享 21%的方差。
平方典型相关通过变量集的潜在表示来表示共享方差,而不是从变量集本身推断的方差。
规范权重
为了获取分配给标准化变量(a1,…,ap)和(b1,…,bq)的权重,我们使用cca.ws
:
cca.ws>> [array([[-0.00375779, 0.0078263 ],
[ 0.00061439, -0.00357358],
[-0.02054012, -0.0083491 ],
[-0.01252477, 0.02976148],
[ 0.00046503, -0.00905069],
[ 0.01415084, -0.01264106]]),
array([[ 0.00632283, 0.05721601],
[-0.02606459, -0.05132531]])]
例如,给定这些权重,集合 Y 的标准变量用以下公式计算:
分配给标准化变量的权重。
其中权重可以被解释为线性回归模型中的系数。我们应该考虑到,在解释单个变量对协变量的贡献时,不建议过分依赖权重。
原因如下:
- 不同样品的重量存在差异。
- 多重共线性对权重的影响很大(对于相同上下文的变量组来说,多重共线性很常见)。
更常见的做法是依赖规范加载。
规范载荷
规范载荷只不过是原始变量和那个集合的规范变量之间的相关性。例如,为了评估信任在学校环境表征中的贡献,我们计算变量集合 x 的变量信任和结果变量之间的相关性。
计算第一个变量中 Y 组的载荷:
print('Loading for Math Score:',np.corrcoef(cca.comps[0][:,0],Y_sc[:,0])[0,1])
print('Loading for ELA Score:',np.corrcoef(cca.comps[0][:,0],Y_sc[:,1])[0,1])>> Loading for Math Score: -0.4106778140971078
>> Loading for ELA Score: -0.4578120954218724
规范协变量
最后,我们可能希望直接访问协变量值,无论是为了可视化还是任何其他目的。
为此,我们需要:
# CVX
cca.comps[0]
# First CV for X
cca.comps[0][:,0]
# Second CV for X
cca.comps[0][:,1]
# CVY
cca.comps[1]
# First CV for Y
cca.comps[1][:,0]
# Second CV for Y
cca.comps[1][:,1]
差不多就是这样!
希望您发现这很有帮助,并在您的 EDA 例程中更多地使用 CCA。
-继续探索
参考:
[1]比连科·n·加兰。,2016,pyr CCA:Python 中的正则化核典型相关分析及其在神经成像中的应用,神经信息数学前沿
2 Dattalo,P.V .,2014 年,使用正交旋转进行典型相关分析的演示,以便于解释
理解假设检验
从抽样分布到中心极限定理再到假设检验
来源: Julius_silver ,via pixabay (cc0)
在这篇博客中,我试图通过一系列的思考过程来理解假设检验。我先问一个问题:
单个样本的平均值如何揭示总体的真实平均值? ”
这些思考过程包括:
1.任意指定一个值作为样本平均值的 采样分布的平均值,****
2.用总体的标准偏差表示抽样分布的标准误差,
3.使用从单个样本计算的标准偏差来近似总体的标准偏差。
我将向您介绍上面使用的所有术语和概念,并用一个示例来说明它们。最后,我将简单讨论一下我在假设检验的背景下学到了什么。尝试写这篇博客对我帮助很大,我希望它也能帮助你。
假设我们的第一个目标是计算出总体中某个参数的平均值。比如韦斯特伍德高中所有学生的平均身高。我们可能不想测量每个学生的身高,而是想随机选择一个样本,例如 30 个学生(样本大小为 30),然后测量他们的身高并计算平均值。为了彻底起见,如果可能的话,我们可能想要重复随机抽样,比如说,20 次以上。请注意,这里允许再次随机选择一名学生。然后,我们可以绘制出我们计算的每个样本平均值的分布图,该图可能有点像图 1 所示。我们称这个图为“ 抽样,样本的分布是指 ”。
图 1:样本均值的抽样分布
在大多数情况下,我们会很偶然地得到一个样本,它的平均高度接近这个抽样分布在中心(= x)的平均值。在少数情况下,我们可能在这条曲线的尾部得到一个平均值(= x)。
L et 提个问题:“如果我们从只取 30 个学生的一个样本开始,计算平均身高(假设是 x),这个平均身高位于图 1 所示尾部位置的概率是多少? 。此时,由于我们没有进行任何其他抽样,我们不知道图 1 中抽样分布(= x)的平均值。我们也不知道分布的 标准差 (说是σx),它代表分布有多宽(可以定义为从曲线中心到频率下降到某个固定值的位置的距离)。似乎我们知道的很少。但如果我们换一种方式问问题呢:“ 如果我们任意给 x 赋一个确定的值,那么 x 位于图 1 所示尾部位置的概率是多少? ”。
让我们看看如何解决这个问题。通常我们用一个参数(说是 z)来描述 x 离 x 有多远,用σx 的单位数来表示,
其中 x 是我们收集的样本(样本大小为 30)的平均值(其值我们已经计算过了),x 是我们任意赋值 的抽样分布 的平均值,σx 是我们希望能够以某种方式估计的分布的标准误差。
有一个强大的等式(源于所谓的“ 中心极限定理 ”)将原始总体 的 标准差(= σ)与样本均值 的抽样分布的 标准差(σx)联系起来当我们一次只对原始总体的一小部分进行抽样(例如一次选取 30 名学生)并多次重复抽样(这样 a
其中σ是我们原始总体(例如,Westwood 高中的全体学生)的标准差,n 是样本量(这里是 30),σx 是我们要搞清楚的样本均值的抽样分布的标准差。
让我们更深入地看看这个等式。在一个极端的情况下,假设我们在抽样中只挑选了 1 名学生(样本量 n = 1),那么平均身高将只是这名学生的身高,抽样分布将只是原始人口的一个试探性复制,因此在这种情况下,σx 将等于σ。在另一个极端的情况下,假设我们对学生群体中的所有学生进行抽样,并计算平均值,那么每次我们都会得到相同的平均值,这就是总体的真实平均值。在这种情况下,分布变为单线,表明σx 接近零。
好的,但是我们仍然不知道σ的值是多少(而且 很可能我们永远也不会确切知道 ),那么我们如何使用等式 2 来估计σx 呢?现在我们需要做一个信念的飞跃。我们仅有的数据是我们从人群中“随机”选择的 30 名学生的身高。现在我们假设我们使用随机过程收集的 30 个数据点最终会得到一个标准差(姑且称之为 S,仅使用这 30 个数据点计算得到),即 近似于 整个总体的标准差(σ)。换句话说,由于这组 30 名学生的选择过程是随机的,这些学生身高的标准偏差将作为整个群体标准偏差的可接受估计。现在我们可以将上面的等式 1 改写为:
其中,我们将等式右侧的所有值(无论是我们从收集的样本中计算的值,如 x、S 和 n,还是 x 的 任意分配的 值)用于计算 z。
现在让我们来看一个真实的例子。我们在样本量 n = 30 的样本中收集的学生身高(以英尺为单位)列于下表 1 中,它们也绘制在图 2 的直方图中(我们稍后将讨论直方图中 5.2 英尺处的橙色线)。样本平均值(x)和标准偏差(S)可以分别计算为 5.56 英尺和 0.63 英尺。
表 1:样本大小为 30 的学生身高样本(英尺)
图 2:样本大小为 30 的学生身高样本的直方图
让我们回到我们提出的最后一个问题,稍微重新措辞一下,“如果我们将某个值指定为图 1 中采样分布的平均值(= x),那么我们得到位于尾部位置(或更远离中心的位置,如图 1 所示)的 x 的概率是多少?”。让我们再换一种说法,如果 x 的值为 5.2 英尺(我们任意指定的值),那么我们获得的样本看起来像我们收集的平均值为 5.56 英尺的样本(或者像任何其他具有更大平均值的潜在样本)的概率(机会)是多少? 。
使用上面的等式 3,我们可以计算出 z 值为(5.56–5.2)/(0.63/sqrt(30))(= 3.15),这意味着 5.56 英尺(= x)的样本平均值位于分布曲线的 0.114 英尺(= 0.63/sqrt(30))的的 3.15 倍处远离该采样分布(x = 5.2 英尺)的平均值。**
关于可能性部分呢?图 1 所示样本均值的抽样分布的本质是什么? 对于 30(或以上)的样本量,分布可以用正态(高斯)分布来表示。神奇的是,不管原始人口分布的形状如何,这都是真的。 正态分布的特征是众所周知的。例如,所有数据位于距中心正负 1 个标准偏差内的概率约为 68%,位于正负 2 和 3 个标准偏差内的概率分别约为 95%和 99.7%。概率的详细值可以在所谓的“Z 表”中找到,如表 2 所示。
表 2: Z 工作台(来自http://www.z-table.com/
在我们的例子中,我们知道我们的 Z 值是 3.15,从 Z 表中我们可以找到相应的概率值 0.9992 (99.92%的几率),这意味着有 99.92%的几率 Z 值小于 3.15 。换句话说,z 值为 3.15 或更大的 几率仅为 0.08% 。另一种说法是,假设 x 值为 5.2,那么我们的“p 值”为 0.0008。
让我们再看一下图 2。上面的陈述表明, 如果我们确实有一个 5.2 英尺的抽样分布平均值(我们假设它非常接近总体的真实平均值),那么我们收集这个平均值为 5.56 英尺的样本(样本大小为 30)的机会加上我们收集任何其他平均值大于 5.56 英尺的样本的机会只有 0.08% ,这是非常小的。也就是说,如果我们执行 1 万次采样,我们只会看到 8 次 5.56 英尺(或更大)的样本均值。所以,我们不太可能以我们收集的样本结束。换句话说,我们有 99.92%的信心不会以我们收集的样本结束。那么,我们任意选择的这个 5.2 英尺(= x)的抽样分布的平均值会不会有点偏差呢?大概如此。
我们注意到,我们任意选择的这个 5.2 英尺(= x)的采样分布的平均值可能是偏离的,并且,如果这个选择的 5.2 英尺的值是真实的,我们有 99.92%的信心,我们不会以我们收集的采样结束。是否拒绝这个 x 值取决于我们想要拥有的 置信度 ,这由我们想要接受的所谓的 置信度 来定义。一个高标准是,如果数据提供了至少 99.87% (z > = 3)的概率,我们可以拒绝我们选择的 x。 如果我们接受 z = 3 的这个值(即 99.87%的置信水平),我们可以拒绝 5.2 的 x,因为我们 99.92%的概率大于 99.87%的置信水平 。然而,如果我们决定选择一个更高的置信水平(比如 99.99%),那么 x 的这个值(= 5.2)是不能被拒绝的,这意味着在 x 的任何假设被拒绝之前,我们希望非常、非常、非常有信心。现在让我们把这个放到所谓的 假设检验 的语境中。
S omebody 这样声明,“ 西林高中学生群体平均身高不超过 5.2 英尺 ”。我们选择勉强不满足上述语句要求的最小可能值,即= 5.2 英尺,并创建一个所谓的“”为“H0: = 5.2 英尺”。如果我们的数据支持拒绝 H0,这表明任何低于 5.2 英尺也被拒绝。为了对抗零假设,我们创建另一个所谓的“ 替代假设 ”作为“ 西林高中学生群体的平均身高大于 5.2 英尺。”(《H1:>5.2 尺》)。在我们拒绝零假设之前,我们希望有 99.87% (z > = 3)的置信度。为了进行调查,我们收集了 30 名学生的样本并测量了他们的身高,结果如表 1 和图 2 所示。从前面几段的讨论中,我们可以得出这样的结论, 仅基于这个样本 ,我们就可以有把握地拒绝零假设,也就是说,我们至少有 99.87%的把握,西林高中学生群体(人口)的真实平均身高大于 5.2 英尺。
如果我们的样本量少于 30 个呢?我们可以按照与上面相同的步骤来计算 z 值,但这里我们只是将值称为“T 值”,并使用另一个名为 T 表(表 3)的表来找出概率。概率列在表的顶行,其对应的 t 值从第二列开始列出。第一列表示一个新参数,称为“自由度”(df),等于样本大小减 1。例如,如果样本大小为 15,则 df 为 14。在这种情况下,样本均值的采样分布的形状作为自由度的函数而变化。从表 3 中可以看出,在这个所谓的“t 分布”中,自由度越小,尾部越重。
表 3: T 表(来自https://www . sjsu . edu/faculty/gerst man/stat primer/T-table . pdf)
L et 再来重温一下本博客开头的第一个问题,
单个样本的平均值如何揭示总体的真实平均值?
似乎单个样本的均值和标准差可以为我们提供一定程度的信心,来判断我们是否可以安全地拒绝假设检验中总体真实均值的假设。
我希望你已经学到了一些有用的东西,或者找到了一些加强你自己信念的想法。请随意评论。
理解基于真实犯罪率的假设检验
统计推断使你能够从数据中得出有意义的结论。统计推断的过程包括首先观察一个模式,然后确定它出现的概率。概率告诉你什么是可能的,什么是不可能的,在此基础上你可以做出合理的判断。
一个非常有用的统计推断工具是 假设检验 。统计学中假设检验的概念是受司法系统中的法律程序的启发。唯一的区别是统计学使用数据进行定量评估;而在法庭上,证据被用来进行定性评估。假设检验的基本思想保持不变。统计中使用的工具经常会妨碍对全局的理解。在司法系统的背景下理解假设检验要容易得多,也有趣得多。这里的目的是使用一个真实的犯罪事件的例子来提供对假设检验的初级理解。
刑事案件
2012 年 11 月 29 日,一群男子闯入住在硅谷的百万富翁科技投资者马赫什·马图尔(Mahesh Mathur)的家。他们把他绑起来,蒙上他的眼睛,用印有小胡子的胶带堵住他的嘴。他们在房子里搜刮现金和珠宝。受害者被发现已经死亡,验尸官后来断定他是被小胡子胶带窒息而死的。三个半星期后,警方逮捕了卢卡斯·安东尼奥(和其他人一起)并指控他谋杀。
定义假设
刑事系统与 无罪推定 一起工作。除非被证明有罪,否则被告永远是无辜的。检察官的工作是通过提供证据来证明被告“有罪”。这有助于我们定义我们的假设,如下。
零假设(H0)
被告在谋杀指控中是无辜的。
替代假设(H1)
被告被指控犯有谋杀罪。
在进行定量统计评估时,研究人员扮演检察官的角色。这似乎违反直觉,但研究人员经常创建零假设,希望能够拒绝它。
显著性水平
下一步是想出一个阈值,在这个阈值我们会拒绝我们的无效假设。这个阈值也称为显著性水平。显著性水平到底是什么意思?
假设检验的最终结果是,我们要么拒绝零假设,要么拒绝失败。(注意,我们从来没有声称 H1 是正确的,但更多的是在以后)。拒绝零假设的决定是基于证据的强度,当然有一定程度的不确定性。我们承认,总是有可能观察到和我们所拥有的证据一样强的证据,只是纯粹出于偶然。这里我们需要问的问题是,在做这个决定时,我们准备承担多大的风险?
显著性水平是我们错误拒绝零假设的风险上限。
研究人员使用的最常见的显著性水平是 0.1、0.05 或 0.01(或分别为 10%、5%和 1%)。在我们的案例中,让我们假设法官将显著性水平固定为 0.05。这意味着法官准备承担 5%的风险对被告作出判决,而实际上他是无辜的。如果你听说过“排除合理怀疑有罪”这句话,当法官做出不利于被告的判决时,他愿意有至少 95%的信心。
注意:我用任意的数字来解释如何用统计假设检验进行定量评估。在现实中,不可能为法庭诉讼的信任程度指定一个固定的数字。
p 值
让我们检查一下检察官提出的证据。
- 在受害者的指甲里发现了被告的 DNA,尽管他们以前从未见过面
- 被告的犯罪历史——几起轻罪
- 一起严重的入室盗窃案
- 无家可归者、精神健康障碍者和酗酒者
我没有法学学位,但对我来说,提出的证据似乎不足以证明被告有罪。甚至被告的 DNA 出现在受害者的身体上也可以有一个合乎逻辑的替代解释(如果这让你感到不安,请继续阅读这个)。如果我们不得不分配一个数字,让我们说所有这些纯粹出于偶然的证据的组合概率是 0.12。这就是所谓的 p 值,即如果零假设为真,得到极端结果的特定概率。
p 值是在零假设为真的情况下,得到我们所拥有的极端结果的特定概率。
在我们的例子中,p 值大大高于预先确定的 0.05 的显著性水平,因此,我们无法拒绝零假设。作为一个语义问题,我们还没有证明零假设为真。我们并没有声称被告是无辜的,但是根据所提供的证据,我们并不认为被告是无辜的。
错误
在之前决定显著性水平时,我们承认存在误差。概括地说,人们需要意识到假设检验可能出现的两种主要错误。
- 第一类错误:拒绝不应该拒绝的无效假设
- 第二类错误:当你应该拒绝一个零假设时,却没有拒绝
在我看来,这两个错误非常容易理解,但很难记住,可能是因为他们懒惰的术语。记住的一个方法是这样想——把一个无辜的人送进监狱可能是最坏的情况,这是第一类错误。
第一类错误是把无辜的人送进监狱。
假设检验通常被设计用来减少 I 型错误。相比之下,第二类错误是释放罪犯。如你所见,这两个错误和“哪个错误更糟糕”的答案之间存在权衡通常根据具体情况而变化。选择适当的显著性水平应基于对 I 型和 II 型误差之间权衡的理解。
至此,我为你搭建了一个舞台,探索使用统计假设检验的定量评估。你可能要了解 中心极限定理 、 正态分布 和 学生的 t-分布 才能充分把握其潜力,但你肯定对假设检验有很强的直觉。祝你好运,阅读愉快!
姓名已被更改,以尊重受影响者的隐私。
参考
- 被自己的 DNA 诬陷谋杀。
- 查尔斯·惠兰的《裸体统计》
了解投资风险
由 Unsplash 上的 Shashank Sahay 拍摄的照片
关于我们如何对投资风险建模以及这是否有意义的哲学探索
在金融中,风险和回报被认为是一枚硬币的两面——你不能缺一不可。回报相当简单。我们买一只股票,等一年,然后查看我们的经纪账户。它增值了,给了我们 10%的可观回报。不错!所以投资的回报,也就是我们赚的 10%,是我们可以直接观察到的。
风险呢?那就不那么明显了。对大多数人来说,投资的风险就是赔钱的可能性。但是事前,那真的很难估计。我们只能看到实际发生的事情。你的投资表现不佳(或表现更佳)的其他替代现实无法直接观察到。我们只看到导致我们获得 10%投资回报的一系列事件。相反,这意味着我们永远也不会知道为了赚取那 10%的利润,我们承担了多大的风险。
风险的驱动因素
投资风险有两个主要驱动因素:
- 特定股票或债券的内在质量或缺乏质量。对一家公司的投资(这适用于股票和债券)是对该公司未来现金流的要求权。这篇文章让我们集中讨论股票。如果我们投资一家公司的股票,那么我们就是在赌这家公司会持续地赚越来越多的钱,让我们在这家公司的股份(购买一家公司的一些股份会让我们拥有这家公司很小一部分的所有权)变得越来越有价值。要做到这一点,该公司既需要继续经营,也需要扩大业务。像谷歌这样拥有强大品牌、资产负债表和商业模式的公司比像 WeWork 这样商业模式薄弱、管理糟糕的公司更有可能做到这一点。因此,如果基础业务的质量下降,那么在其他条件相同的情况下,这将增加投资的风险。
- 我们分析的不确定性,也就是出错的风险。当我们分析一家公司时,我们永远不能确定我们已经发现了一切,或者我们已经正确地分析和理解了一切。如果我们错误地将一个实际上脆弱的商业模式归类为强大,那么我们的投资业绩将受到影响。
对一项投资进行评估既困难又耗时,更不用说数百甚至数千项投资的投资组合了。因此,在定量金融中,投资回报的标准差(通常被称为其波动性)通常被用作其风险的近似值。
计算详情: 为了估计一项投资的风险(即波动性),我们会收集几年的周收益数据(每次观察都是投资在指定周内的收益)并取其标准差。如果我们想按年计算,那么我们将把计算值乘以 52 的平方根。
标准差作为风险的近似值
让我们首先思考为什么标准差可能被用作风险的近似值。在统计学中,我们对某事物估计的标准差越高,我们就越不确定它的真实价值。
看看下面的两条钟形曲线。它们有不同的标准差,在图上显示为不同的分布宽度。当分布很窄时,这意味着真实值位于一个很小的范围内——或者换句话说,我们非常确定值是多少。当分布很宽时,我们很不确定值是多少,必须考虑它可能等于任意数量的值的可能性。
钟形曲线比较
所以在统计学中,大的标准差等同于高的不确定性。但这真的有风险吗?让我们用一个例子来更深入地考察这一点。假设我们认为股票投资组合的预期年回报率为 7%,标准差为 14%。
这意味着,虽然我们预计股票回报通常为正,但也有很大的可能最终为负(根据我们的统计假设,有 30%的可能性是准确的)。因此,在其他条件不变的情况下,一项投资的标准差越高,其回报最终可能为负的可能性就越大。但是波动是对称的——虽然有 30%的机会经历负回报,但也有 30%的机会获得 14%或更高的回报:
波动对称地起作用
此外,不要忘记任何正态分布的随机变量都有两个关键属性——均值和标准差(关于正态分布的复习,请查看我之前的博客)。在这种情况下,平均值是我们投资的预期回报率(7%)。在金融领域,我们在计算投资风险时通常会忽略均值,而只关注标准差。如果我们退一步想想,这似乎违背了直觉。看看下面两个描述两种不同投资的风险和回报的直方图?哪个看起来风险更大?
投资 A 与 B
金融理论会告诉我们,投资 B 的风险更大,因为它的分布更广(标准差更高)。但它也有更高的回报,所以每年经历负回报的机会低于投资 A。所以就尽量不亏损而言,投资 B 其实是更安全(风险更小)的选择。关键是,如果投资的预期回报足够高,即使是非常高的标准差(相对于其他投资)也不是什么大不了的事情——尽管你最好再三检查你是如何估计预期回报的。因此,如果我们将风险视为赔钱的概率,那么相对于其标准差而言预期回报较低(且产生负回报的概率较高)的投资才是真正有风险的投资。因此,看起来一项投资的预期回报从根本上与该项投资的风险息息相关。
也就是说,在计算风险时,我们需要同时考虑均值和标准差。
那么,金融业是如何忽略一项投资的预期回报,只使用其标准差来估计风险的呢?
有效市场
这是因为模型假设市场通常是有效的。这意味着金融市场作为一个整体(所有投资者的总和)被认为在资产定价方面做得很好。
在其他条件相同的情况下,你为一项投资付出的价格决定了它的回报。如果你花 200 万美元买一家麦当劳餐厅,你可能会获得可观的回报(在很长一段时间内)。如果你能以 100 万美元买下同一家餐厅,你将获得更高的回报。如果你被骗了 400 万美元,你可能永远也赚不回你的初始投资。
因此,如果市场在吸收信息和评估风险方面做得很好,那么可以得出结论,它们在投资定价方面也做得很好。如果他们正确地为投资定价,那就意味着资本市场也正确地评估了预期回报。
现在让我们想想这意味着什么。在一个市场正确分配价格(和回报)的理想世界中,这些价格将如何设定?价格会根据投资者最关心的因素而变化。金融理论假设所有重要因素都是风险——投资风险越大,价格越低(相对于其产生的现金流而言),预期回报越高。也就是说,假设风险和收益之间存在大致线性的关系:
随着风险的增加,投资者被认为会要求更高的预期回报作为补偿
如果你仔细想想,这是一个非常简化的捷径。通过假设市场是有效的,我们不再需要担心如何计算预期回报——相反,我们依赖于一个基本信念,即投资的风险决定其价格,从而决定其回报。或者换句话说,预期收益被认为是波动率的一个明确定义的函数,而不是别的。如果我们假设波动性决定回报,那么在分析投资风险时,我们现在可以安全地忽略预期回报部分。
所以有效市场会告诉我们投资 B 是不可能的。要么我们计算的回报率太高,要么标准差太低。与投资 A(以及世界上的其他投资)相比,投资 B 的风险和收益特征“与现实脱节”(违背了功能),因此是不可能的。毕竟,正如市场效率的信奉者喜欢说的那样,“没有免费的午餐”。
但是有意义吗?
有效市场的前提是强大的。相信市场效率基本上是相信群众的智慧。数以百万计的专业人士已经花了无数个小时研究这些数据——所以你认为你对一只股票可能有的任何洞察力实际上都不是洞察力,而是已经被纳入股票价格的信息。此外,只要存在意见的多样性,市场的单个成分所犯的错误就会多样化(类似于随机森林),整体将产生平均正确的输出。
从高层次来看,这听起来确实有道理,但这里有一些反驳意见,或许可以解释为什么市场可能不总是最好的价格制定者:
- 人群的疯狂也适用。当意见多样化时,错误也会多样化。但是,如果没有多样性,每个人或多或少都相信同样的事情,错误就会扩大。比特币这样的投资泡沫就是这样产生的。
- 不是所有的投资都存在于一个有足够多的买家和卖家(或数据)来有效定价的市场中。比如,我会把初创公司股权市场描述为极度缺乏流动性、不透明和低效的(任何不幸在 2018 年或 2019 年获得 WeWork 股票报酬的人可能都不认为他们的执行价格是有效的)。就观点和意见而言,它也是一个极度缺乏多样性的国家。
- 在繁荣时期,市场往往会逐渐上涨(伴随着波动性的逐渐下降)。因此,随着对上次崩盘的记忆逐渐消失,经济扩张成为我们样本中相对较大的一部分,我们的模型将开始低估风险。虽然你可能会说我们更关心相对风险(一项投资的波动性与另一项投资或整体市场的波动性相比如何),但金融业确实使用绝对波动性和从绝对波动性得出的指标来进行风险预算(即计算出多少钱用于风险较高的投资,多少钱作为无风险现金)。因此,随着波动性下降(通过向股票等风险更高的投资增加资金)和下一次衰退的逼近,投资经理承担越来越多的风险是有危险的。
- 市场崩盘发生得非常快,通常以周计,如果不是几天的话。崩盘的巨大负回报通常会导致波动性飙升,触发各地的风险警报。突然之间,各地的投资经理都认为他们的投资组合风险极大(因为投资的波动性和投资之间的相关性增加了如此之多),并疯狂地尽快抛售高风险投资。
- 相关性上升对投资组合总风险的影响可能是巨大的。那些在繁荣时期被证明分散投资于你的美国股票的泰国股票,在市场崩盘时,几乎肯定会和你所有的其他股票投资一起下跌。在危机中,相关性达到 1。
注意到我在第 3 和第 4 条中描述的行为有些奇怪吗?这是典型的高买低卖。回想起来,那不是非常理性或有利可图的行为。但这是恐惧和波动等于风险的信念的副产品。发生这种情况的另一个原因是,随着经理们争先恐后地向上修正他们的风险估计,他们保持他们的预期回报估计不变。这使得一般的风险投资看起来不那么有吸引力,因为它突然有了更高的波动性,但仍然有相同的预期回报。
实际上,有一种更明智的方式来处理事情,不需要我们打破波动性和风险之间的联系(并抛弃所有简化的好处)。波动性是一个动态值,随着投资价格的波动而不断变化。
我们应该像看待保险报价一样看待波动性,而不是将波动性视为对资产风险的公正而准确的估计。比如说,飓风保险的价格主要取决于两个因素——对灾难风险的感知(这影响到希望购买保险的人数,也就是需求)和愿意承保的公司数量(供应)。同样,在任何时间点,投资的波动性是两个因素的函数——投资的感知风险和愿意承担风险的人数。
飓风过后,每个人都意识到风暴的破坏力有多大,因此对保险的需求非常高。与此同时,在阳光明媚的时期以过低的保费签订了过多合同的保险公司正在遭受打击,因此保险供应非常低。如果你能够在这个时候介入提供保险,你可能会获得巨大的财务回报,因为你可以赚取极高的保费。
在金融领域也是如此。当一项投资的波动性发生变化时,这是因为投资的感知风险随着愿意承担该风险的投资者数量的变化而变化。在繁荣时期,强劲的增长和利润降低了股票等投资的风险,强劲的回报让越来越多的投资者愿意持有股票。所有这些的净效应是,只要好日子还在,波动性就会持续下降。但就像保险一样,如果没有人关心股票的风险,每个人都愿意持有股票,那就意味着股票价格异常高,而他们在中短期内的预期回报将异常低。当市场崩溃、公司利润锐减时,情况就会相反。现在,股票将被视为风险极高,同时有意愿持有股票的人急剧减少(由于恐惧、失业等原因)。)—使得预期股票收益畸高。
如果我们真的相信风险和回报之间存在联系,那么随着波动性下降,我们应该同时降低对预期回报的估计。这种预期回报的降低将抵消波动性的降低,并抵消购买更多股票的冲动(它甚至可能使我们想要减少我们的股票持有量)。当波动性飙升时,我们应该向上修正我们对预期回报的估计,这将消除我们低价卖出的冲动,甚至可能促使我们低价买入。
结论
投资风险是一个极其复杂的话题,也是一个经常争论的话题。在这篇文章中,我尽我所能给你一个 10,000 英尺的视角来看事物是如何工作的。我不同意投资风险理论的某些部分,但总的来说,它是一个非常有用的工具,涉及现代金融的许多方面。但是就像我们决定开始使用的任何其他工具一样,我们应该首先了解它的局限性。我希望这有所帮助。干杯!
了解 K-Means、K-Means++和 K-Medoids 聚类算法
K-means、K-means++和 K-Medoids 聚类算法及其关系概述。本文还包括它的从头开始的实现和使用 sklearn 库。
卢卡斯·布拉塞克在 Unsplash 上的照片
聚类是一种无监督的机器学习技术,它将群体或数据点分成几个组或聚类,使得相同组中的数据点与相同组中的其他数据点更相似,而与其他组中的数据点不相似。
- 同一簇中的点彼此更接近。
- 不同簇中的点相距很远。
来源,样本数据点,(图 1)
在上面的二维数据集样本中,可以看到数据集形成了 3 个相距很远的聚类,并且同一聚类中的点彼此靠近。
聚类的衡量标准是什么?
如果聚类具有最大的簇间距离和最小的簇内距离,则被认为是最佳的。
***Notes to avoid any confusion:*****Intracluster distance:** Distance between two point in the same cluster.
**Intercluster distance:** Distance between two points in the different clusters.
(图片由作者提供)、簇内和簇间距离(图片 2)
上图(图 2)描述了什么是簇间和簇内距离。
可以使用各种评估度量来测量形成的集群的有效性。
邓恩指数:
上述函数的分子测量属于两个不同聚类的每两个点(x_i,x_j)之间的最大距离。这代表星团内距离。
上述函数的分母测量属于同一聚类的每两个点(y_i,y_j)之间的最大距离。这代表集群间距离。
具有邓恩指数最大值的聚类被认为是最好的。
剪影分析:
这用于确定聚类之间的分离程度。对于每个样本。 a_i 表示同一个聚类中所有数据点的平均距离。 b_i 代表离最近聚类中所有数据点的平均距离。
SA 的系数可以取区间[-1,1]内的值。
- SA = 0:样本非常接近相邻的聚类。
- SA = 1:样本远离相邻聚类。
- SA = -1:样本被分配到错误的聚类。
因此,我们希望系数尽可能大,并且接近 1。
有不同类型的聚类技术,我们将讨论其中的一种。
k 均值聚类:
K-Means 算法是一种基于质心的聚类技术。该技术将数据集聚类成 k 个具有几乎相等数量的点的不同聚类。每个聚类是用一个质心点来表示的 k-means 聚类算法。
什么是质心点?
形心点是代表其群集的点。形心点是集合中所有点的平均值,将在每一步中发生变化,计算方法如下:
For the above equation,
C_i: i'th Centroid
S_i: All points belonging to set_i with centroid as C_i
x_j: j'th point from the set
||S_i||: number of points in set_i
K-Means 算法的思想是找到 K 个质心点,并且数据集中的每个点将属于具有最小欧几里德距离的 K 个集合中的任一个。
图 3
根据上图(图 3),点 x_i 到所有三个质心的距离为 d1、d2、d3,点 x_i 距离质心 _3 最近,距离为 d3,因此点 x_i 将属于质心 _3 的聚类,并且该过程将对数据集中的所有点继续进行。
K 均值的成本函数:
K-Means 算法的思想是找到 K 个质心点(C1,C1,.。。C_k)通过最小化每个聚类上该点与其质心之间距离的平方和。
这个代价是 NP 难的,并且具有指数级的时间复杂度。所以我们使用劳埃德算法的近似思想。
劳埃德算法:
劳埃德算法是一种用于聚类点的近似迭代算法。该算法的步骤如下:
- 初始化
- 分配
- 更新质心
- 重复步骤 2 和 3,直到收敛。
K 均值算法的迭代实现:
步骤#1:初始化:
初始 k 形心是从数据集中随机选取的(第 27-28 行)。
步骤#2:分配:
对于数据集中的每个点,找出该点和所有质心之间的欧几里德距离(第 33 行)。该点将被分配给质心最近的聚类。
步骤#3:质心的更新:
用新的平均值更新质心的值(第 39–40 行)。
步骤#4:重复:
除非达到收敛,否则重复步骤 2 和 3。如果实现了收敛,则中断循环(第 43 行)。收敛是指质心的先前值等于更新值的情况。
结果:
初始数据集的绘图(图 4)
数据集的图(图 4)
k=2 的聚类结果图(图 5)
k=2 时的聚类图(图 5)
k=3 的聚类结果图(图 6)
k=3 时的聚类图(图 6)
K-Means++聚类:
在使用劳埃德 K 均值聚类算法寻找初始质心的情况下,我们使用了随机化。初始的 k 形心是从数据点中随机选取的。
这种选取 k-质心点的随机化导致了初始化敏感性的问题。这个问题倾向于影响最终形成的簇。最终形成的聚类取决于初始质心是如何选取的。
以下是一些聚类的结果,其中质心的初始化是不同的:
通过不同的初始化形成不同的最终聚类(图 7)
在上图(图 7)中,最终形成的簇是不同的,因为最终形成的簇依赖于质心的初始化。在上面图像的第一部分,可以观察到质心(黑色)和星团没有正确形成。在上面图像的第二部分,可以观察到质心(黑色)和星团的形成。
有两种方法可以避免初始化敏感性的问题:
- 重复 K-means: 多次重复算法和质心初始化,选择簇内距离小、簇间距离大的聚类方法。
- K-Means++: K-Means++是一种智能质心初始化技术。
以上两种方法可以用来避免初始化敏感性的问题,但在这两种方法中,K-Means++是最好的方法。
K-Means ++是如何工作的?
K-Means++是一种智能质心初始化技术,算法的其余部分与 K-Means 相同。质心初始化的步骤如下:
- 随机选取第一个质心点(C1)。
- 计算数据集中所有点与所选质心的距离。x_i 点到最远质心的距离可以通过下式计算
d_i: Distance of x_i point from the farthest centroid
m: number of centroids already picked
- 将点 x_i 作为新的质心,它具有与 d_i 成比例的最大概率。
- 重复上述两个步骤,直到你找到 k 形心。
使用 sklearn 实现 K-means++:
上面我们已经从头开始讨论了 K-Means 的迭代方法,为了实现 K-Means++算法,我们将使用 sklearn 库。
实施和结果演练:
- 加载或创建数据集(第 8 行)
- 为数据集实现 k-means++算法(第 9 行)
- 绘制原始数据集以观察数据集(第 25–26 行)。
数据点图(图 8)
- 找到 k 形心点
- 绘制聚类结果(圆形)和 k 形心(红色*)(第 29-32 行)。
聚类结果图(图 9)
K-Medoids 聚类:
K-Means 和 K-Means++聚类的一个问题是最终的质心是不可解释的,或者换句话说,质心不是实际的点,而是该聚类中存在的点的平均值。这是不像数据集中真实点的 3-质心坐标。
K-Medoids 聚类的思想是将最终的质心作为实际的数据点。这个结果使形心可以解释。
K-Medoids 聚类的算法被称为 Medoids 周围划分(PAM ),它与 Lloyd's 算法几乎相同,只是在更新步骤上略有变化。
PAM 算法要遵循的步骤:
- 初始化:与 K-Means++相同
- 赋值:同 K 均值赋值
- 更新质心:在 K 均值的情况下,我们正在计算聚类中所有点的均值。但是对于 PAM 算法,质心的更新是不同的。如果在一个群集中有 m 个点,则用该群集中的所有其他(m-1)个点交换先前的质心,并将该点最终确定为具有最小损失的新质心。最小损失通过以下成本函数计算:
- 重复:与 K 均值相同
如何挑选 K 的最佳值?
K 的最佳值可以使用弯头法计算。K-Means、K-Means 和 K-Medoids 技术的成本函数是最小化簇间距离和最大化簇内距离。这可以通过最小化上面文章中讨论的损失函数来实现:
为了确定正确的“K ”,在损失和 K 之间画一个图。
损耗与 K 的关系图(图 10)
对于上述曲线,可以观察到随着“k”值的增加,损耗减少。为了找到绘制 k-聚类的最佳 k 值,我们可以选择 k=3。
感谢您的阅读!
了解 K-最近邻
kNN 的分解及其工作原理!
图片作者:特里斯特·约瑟夫
机器学习(ML)算法通常分为监督的或非监督的,这广义上指的是正在使用的数据集是否被标记。监督 ML 算法通过使用带标签的例子来预测未来的结果,将过去学到的知识应用到新数据中。本质上,对于这些类型的问题,正确的答案是已知的,并且基于预测的输出是否正确来判断估计模型的性能。
相比之下,无监督 ML 算法指的是当用于训练模型的信息既没有被分类也没有被标记时开发的算法。这些算法试图通过提取样本中的特征和模式来理解数据。
当给定的任务是分类或回归问题时,监督学习是有用的。分类问题指的是根据模型开发的特定标准将观察结果或输入数据分组到离散的“类”中。一个典型的例子是预测一张给定的图片是否显示一只猫。该模型将在包含猫图片和非猫图片的数据集上开发和训练,其中每个观察都被适当地标记。
图片作者:特里斯特·约瑟夫
虽然监督学习对于分类和回归问题都是合适的方法,但是这两者是非常不同的。回归问题是指接受一组输入数据并确定一个连续量作为输出的过程。也就是说,回归问题的目标是在给定一个输入向量的情况下预测一个特定的数字。而另一方面,分类问题预测与输入向量相关联的离散值(或标签)。
通常,分类算法基于相似性将观察结果分组在一起。例如,假设我们想将爬行动物和非爬行动物进行分组。人们可能会发现,有鳞、产卵、有毒的动物更可能是爬行动物,而不是非爬行动物。因此,如果将具有这些特征的新观察结果添加到数据集,相似性分析将发现该观察结果与爬行动物的关系比与非爬行动物的关系更密切,并且该观察结果将被归类为爬行动物。
图片作者:特里斯特·约瑟夫
要进行的一种常见的相似性分析是距离矩阵的分析。这表明,就它们的共同点而言,观测结果彼此之间相距有多远。观测值之间的距离越小,它们之间的相似性就越大,被组合在一起的可能性就越大。确定距离有多种方法,但一种相对简单的方法是 Jaccard 指数。
该指数也称为 Jaccard 相似系数,它比较两个组的成员,以查看哪些成员是相同的,哪些是不同的。相似性的度量范围从 0%到 100%,百分比越高表示相似性越大。回到爬行动物与非爬行动物的例子,假设相关特征是鳞片、有毒、产卵、有胡须和有血。如果我们把一条蝰蛇和一个人相比,他们可能只有 1/5 的共同点,相似度为 20%。因此,不应将它们归为一类。
图片作者:特里斯特·约瑟夫
这个例子是最简单的分类方法之一,它被称为最近邻。该算法的“学习部分”相当简单,因为该算法所要做的就是记住训练数据。然后,当一个新的观察值需要分类时,算法就会转到训练数据,看这个新的观察值与之前的哪个观察值最相似,然后预测它为那个组。
从表面上看,这种最近邻方法似乎很壮观,因为很可能彼此最相似的观测值应该被分组在一起。从上面的例子中我们看到,蝰蛇和人类没有太多的共同点,所以它们被归为爬行动物的可能性很小。但是回想一下,我们的任务是预测爬行动物和非爬行动物。鱼应该归入非爬行动物类,但也有有毒的鱼,有鳞,会产卵。基于相似性分析(以及前面给出的变量),鱼将被归类为回复。
因此,最近邻方法的问题是它对噪声数据非常敏感,可能导致不正确的分类。
图片作者:特里斯特·约瑟夫
通常解决这一问题的办法是修改最近邻法; k 最近邻(kNN) 。这里的想法是,我们不只是取最近的邻居,而是取一些最近的邻居(通常是奇数),并让他们“投票”预测分类应该是什么。
那么现在问题来了,我们如何选择合适的 k 呢?嗯,有三件主要的事情需要考虑。第一,随着 k 变大,算法运行的时间越长,因为它必须进行更多的比较。当训练集包含更多观察值时,情况也是如此。第二,如果 k 太小,那么邻居可能没有办法“投票”选出一个获胜者。因此,k 必须足够大。最后一点是,如果 k 太大,就会有被班级规模所左右的风险。也就是说,假设在我们的集合中有许多种类的爬行动物,并且爬行动物的数量超过了非爬行动物。鉴于 k 非常大,一个新的观察结果可能会被归类为爬行动物。
因此,选择适当 k 的最可靠方法是交叉验证。这是一种用于评估模型拟合度的技术,通过在样本数据集的不同子集上训练几个模型,然后在训练集的互补子集上评估它们。
图片作者:特里斯特·约瑟夫
kNN 的优点是学习速度非常快,它不涉及太多的数学,并且非常容易解释过程或算法得出的结果。然而,缺点是它非常占用内存,并且由于进行了大量的比较,产生预测需要很长时间(随着数据集变大)。
参考资料和其他有用的材料:
OCW . MIT . edu/courses/electrical-engineering-and-computer-science/6-034-artificial-intelligence-fall-2010/tutorials/MIT 6 _ 034 F10 _ tutor 03 . pdf
jmlr.org/papers/volume10/weinberger09a/weinberger09a.pdf
geeks forgeeks . org/find-the-JAC card-index-and-JAC card-distance-between-the-two-given-sets/
towards data science . com/machine-learning-basics-with-k-nearest-neighbors-algorithm-6a6e 71d 01761
analyticsvidhya . com/blog/2018/03/简介-k-邻居-算法-聚类/
sci kit-learn . org/stable/modules/generated/sk learn . neighbors . kneighborsclassifier . html
理解卡夫卡,就像是你设计的一样——第一部分
由内向外理解阿帕奇·卡夫卡
阿帕奇卡夫卡已经成为 IT 驱动的企业的支柱。从实时消息传递到长期存储,它在处理大数据的所有“3v”环境中几乎无处不在,即容量、多样性和速度。
作者图片
尽管它的 API 很容易理解和使用,但是它的内部结构并不简单。和往常一样,你不需要了解引擎就能成为一名优秀的飞行员,然而,更深入的研究可以区分优秀和优秀。
这是解释卡夫卡背后的构建模块的两部分系列的第一部分。这一部分将关注其主要动机,以及每个代理如何在内部存储和管理数据。第二部分将关注可伸缩性和持久性的元素。
我们将从两个非常好且雄心勃勃的朋友的角度来进行我们的探索。
创业驱动
你的一个好朋友开始在市气象部门工作。和任何新加入者一样,他充满热情和动力去完成事情。
他有一个新的实时天气预报方法的想法,但意识到遍布城市的温度计数量远远不够。他去和他的上司谈了谈,他的上司告诉他——尽管他对这个问题并不了解——并不是缺少设备,而是不可能实时处理这么多数据。但他了解你,对你来说,技术上的不可能性只不过是一个机会。
要求很简单:每两秒钟从温度计接收一次数据,然后 1)存储这些数据以供进一步研究,然后 2)将它们发送到他伟大的新模型。
你的需求草图
第一次尝试
一切都很清楚。您只需要一个单一的过程来接收测量值,存储它们,然后发送给模型。然而,由于您非常熟悉单一责任和接口分离等概念,您决定将系统解耦为两个部分: 1) 一个从温度计接收测量值、附加时间戳并存储它们的过程,以及 2) 一个每两秒钟查询一次数据库的过程,将消耗的最后一个时间戳作为偏移量传递,并将结果发送到您朋友的模型。你的问题太简单了,最难的是选择使用哪种开源 RDBMS。
在选择了您喜欢的 RDBMS 之后,您通过创建两个类开始了这个项目,一个接收测量值并存储它们,另一个读取存储的测量值并将它们发送到模型。尽管两者都将从某个地方读取和写入,但是您决定从存储解决方案的角度来命名它们,因此接收度量值并将它们写入 RDBMS 的类被命名为 Writer ,而读取度量值并将它们发送到模型的类被命名为 Reader 。
您还创建了一个名为 Measurement 的类来保存您正在处理的数据。然而,由于您喜欢习惯性的,您决定遵循通常与 RDBMS 条目相关的命名,即记录。这样你也可以让你的设计更加通用。
没费多大力气,您的第一个解决方案就准备好了:一个将记录写入您的存储解决方案的写入器和一个从那里进行消费的读取器。
你的第一次尝试
庆典和天亮后
一个星期后,你带着完全可操作的解决方案回来找你的朋友。你按下回车键,他的模型开始每两秒钟左右记录一次。他请你喝啤酒,他请客。在庆祝期间,他承认他的模型做得比预期的好,也许它能够每 1 秒钟处理一次传入的记录(他也认为记录是个好名字)。
这并没有让你感到惊讶,因为你已经习惯了需求的变化。但这次已经解决了。你所要做的就是将配置从 2 秒钟改为 1 秒钟,这样就完成了。
第二天早上,你只需将计时器从 2 秒钟改为 1 秒钟,它就能持续工作,他的模型也是如此。他花了一天时间对它进行更多的调整,到一天结束时,它变得疯狂,能够以流的方式每秒处理数百万条记录,即记录不再需要每隔 X 秒发送一次,而是从温度计接收到记录后立即发送。
为了满足这个新的请求,您考虑过用合并写进程和读进程,这样写进程就可以存储记录并在之后发送消息。但是您考虑到了在失败或过载的情况下应用重试和反压会有多麻烦。然后,您考虑在两个进程之间创建某种并行通信,这样写者可以存储记录并通过单独的队列将其发送给读者,这样读者就不需要为了获得基于时间戳的记录而查询 RDBMS。但这意味着您的解决方案会更加复杂。所以你只是从阅读器进程中移除了计时器。通过这种方式,它查询 RDBMS,获得最新插入的记录,并将它们作为一个批处理发送出去。由于您选择的存储解决方案的吞吐量相当不错,因此它是可行的。
碰壁
几天后,你的朋友打电话给你。这个解决方案——以及他的名声——迅速走红,让人们如此感兴趣,以至于他们现在正在订购一种新型温度计,这种温度计能够采集更多的数据。你非常高兴你的解决方案为你朋友的梦想提供了动力,以至于你没有立即注意到这个问题。相反,你得到了一个新温度计的样品,并让它发送一些测试数据,只是为了看看你的解决方案,第一次,失败了。
经过一些调试后,您找到了问题的根源: 新温度计使用的模式与 不同。你感到一阵颤抖。如果每个温度计都有不同的模式会怎样?如果是这样的话,从可维护性的角度来看,您的解决方案将注定失败。你应该为每一种新的温度计添加一个新的表格吗?你打电话给你的朋友,他证实了这一点。他还问是不是主要问题,你否认了。这不是问题,只是一个机会。
问题:每个新传感器都需要新的模式
更深的潜水
你发现自己处于以下情况:
1.您有两个进程,写进程和读进程,它们从给定的存储解决方案中存储和检索数据
2.您的存储解决方案需要预先知道模式,以便在编写时应用该模式
3.您不需要任何特殊的处理来写入记录,只需要按原样存储它们
4.您不需要任何特殊的查询或过滤功能,只需要能够按照记录到达的顺序检索每条记录
5.您只需要在检索记录时,即在读取记录时,知道模式
在对当前事态进行了大量思考之后,您记得曾经考虑过实现某种队列,以便将记录从作者直接传递给读者。如果该队列以某种方式通过一个字段(时间戳)持久化并可搜索,您将接近满意的解决方案,因为您可以 1) 保留您已经拥有的流程, 2) 将记录存储为二进制对象,这样它们的模式在写入时就无关紧要了, 3) 检索在给定时间戳之后存储的所有记录, 4) 在分派之前应用模式
这听起来很有希望,但从某种意义上来说,你是在提出一种新的存储解决方案,这也应该带来可伸缩性、弹性、容错等方面的需求。那将是很大的工作量,你的朋友也会同意。
政治家
第二天早上,你邀请你的朋友去喝啤酒(这次你请客)。在信息中,你提到一个革命性的想法打动了你,你必须分享它。
目前的情况
“你知道,在数据库领域,关于如何将模式应用于记录有两种想法:在写入期间和在读取期间。我称它们为 写模式 和 读模式 。前者使执行查询更快,因为您可以利用格式和数据类型,但它也有点束缚了您。每次你需要改变它的时候,整个系统都会遭到破坏。另一方面,后者让您可以根据自己的意愿灵活地更改数据,这对于我们正在进行的创新至关重要。查询变得有点困难,但是我们不需要任何特殊的过滤,只需要时间戳。你知道,我们可以继续像现在这样做,但我们在这里不是为了小壮举,是吗?所以,想想这个。现在,我们接收记录并将其发送到 RDBMS,RDBMS 将其写入事务日志,然后写入最终存储。只有在那之后,我们才能访问数据。如果我们需要对记录进行一些额外的过滤,那是没问题的,但是我们不需要。我们唯一需要的是在记录被保存到日志后立即获取它们,就像跟踪它一样。我们绝对可以应用一个名为 的花哨想法,更改数据捕获 并直接从那个日志中检索数据,但如果我们实现自己的日志,我们可以根据自己的需要优化它,我们可以同时在您的模型和我的解决方案上取得两个突破”。
你的朋友被迷住了,不仅被这个想法迷住了,还被他不知道的术语迷住了。他喝了一小口,问了一个重要的问题:我们需要什么?。你的回答很及时:就像生活中所有美好的事物一样,时间和金钱。你的自信迫使你的朋友承认,由于你们的共同努力,他得到了提升,现在他也有了研究预算。头奖!
棘手的问题
随着你朋友的一声“开始”,现在是时候把事情做完了。
您将开发一个类似日志的存储,它接收记录、附加时间戳并保存它们。在阅读器端,最常见的用例是跟踪日志,这可以通过利用页面缓存和避免磁盘寻道来有效地完成。因此,在某种意义上,您需要一个键值存储,其中时间戳将是写入和读取的关键。
你有什么想法
乍一看,这很简单,但是更新和删除呢?酸担保?时间戳的粒度应该是多少,秒、毫秒、纳秒?如果您需要检索一个特定的记录或从该记录重新开始呢?由于它的复杂性,开始感觉有点令人生畏,但是你知道方法:分而治之。
首先要做的事情
经过一番思考,您意识到即使记录与时间戳相关联,这也不是用作键的最佳数据类型。出于您的目的,时间戳代表到达的顺序,但是如果您只是在记录到达时为每个记录分配一个单调递增的标识符,那么您会获得完全相同的效果。
因为您正在向日志中添加记录,所以该标识符可以是一个长值,包含从日志开始的偏移量,其偏移量为0。长值比时间戳更容易引用。现在,您的编写器和读取器将只保留对最近处理的偏移量的引用。写入方将增加 1,并将其分配给每个新记录,读取方也将增加 1,以检索下一个记录。
更好的方法
考虑到这个更好的可访问性设计,是时候决定如何实际存储记录了。无论如何,它们都必须存储在磁盘上。如果您能保证每当需要重新处理时,整个记录集都会重新处理,那么您可以将所有记录存储在同一个文件中。但是如果需要从一个特定的偏移量开始呢?如果所有的消息都存储在同一个文件中,您就必须从头开始扫描,直到达到那个偏移量,这种效率很低,与突破性进展不太相容。
分得更多
你知道随机存取文件以及它们支持从特定偏移量读取和写入的能力。既然您已经在使用偏移量的概念来标识您的记录,那么您想到了一个聪明的主意:如果您创建两个文件,一个用于您的记录,,另一个用于偏移量,会怎么样?
如果你这样做,那么你的偏移文件将只包含关于记录偏移的信息以及它们在记录文件中的开始和结束位置。这个偏移文件可能保存在内存中,或者如果没有,可以很快加载。现在,您的编写器只需追加到这两个文件中,如果有人需要从特定的偏移量检索记录,您的阅读器可以读取偏移量文件(如果在页面缓存中还没有找到记录),在记录文件中找到开始和结束位置,然后砰!
你也认为偏移文件已经是一个足够有特色的名字,但是记录文件听起来没有那么好。经过一番思考,您意识到您的记录文件实际上是您试图实现的日志。清清楚楚!
Writer: 1)写入日志,2)索引偏移量;阅读器: 1)查找偏移量,2)读取记录
当激励的气息袭来时,你决定走得更远,也允许时间的恢复。您所要做的就是遵循与 offsets 文件相同的推理,也就是说,如果您添加另一个包含记录到达时间的时间戳和 offsets 文件中与该记录相关联的偏移量的文件,您也可以以非常高效的方式从特定时间戳检索记录。这个文件将作为记录到达时间的索引,所以您认为 timeindex 是一个好名字。
您现在有了一个解决方案,可以将偏移量附加到记录上,并在记录到达时将它们保存到一个名为logfile 的持久存储中,并在几乎相同的时间使用它们。您也可以从偏移文件中以非常有效的方式获得任意偏移的记录。最后,您甚至可以通过使用 timeindex 文件获得基于到达时间的记录。
解决了插入和查询
但是更新和删除呢?
艰难的决定
这次你似乎碰到了一堵墙。如果您想提供更新功能,您还需要以下内容:
1.一种改变记录文件的有效机制
2.偏移文件的更新
从最后一个开始,改变偏移文件并不是什么大不了的事情。更改记录文件也是可行的,但是,您最终会遇到两个问题之一:更新所有其他记录的开始和结束位置,或者浪费空间。这两种方式都与你所追求的突破不相容。
然后你意识到一些事情,一些人后来会称之为一些有趣的流表二元性理论的一部分。你正在开发的发明不需要更新,因为对一个事件的*更新只是另一个事件 。从这个角度来看,更新只是在日志尾部添加一个新事件,这是你的发明已经支持的。答对了。现在您可以说您有一个只附加日志,记录按到达日期排序。你已经能感觉到炒作了!*
最后一次除法
兴奋占据了主导,但是删除怎么办?
有一个只附加的系统是好的,因为更新只是新的事件,但是删除是必须的,否则磁盘会和你的梦想一起死去。
如果你通过允许修改日志来支持删除,你在更新中发现的同样的问题将会出现,而且你将不能再称它为仅追加,缺少一个吸引人的词来进一步宣传。
你回家后无法入睡,只是想着这个难题。最终你的大脑会关闭一段时间,这段时间足够你梦到大毒蛇了。你兴奋地醒来。就像蛇一点一点地吃掉自己一样,你的发明应该一点一点地吃掉自己的木头。只缺两样东西:棋子和午餐时间。
征服
在感谢伟大的修普诺斯,睡眠之神之后,你回到你的电脑前。你需要你的日志碎片,这意味着它应该成为许多。你需要决定这些部分可以有多大,什么时候它们必须消失。关于后两者,到最后,你意识到它更多的取决于系统被使用的上下文,而不是你的意愿,所以你决定把控制权交还给用户。
您的新方法将创建多个日志文件,而不是一个,每个日志文件都包含自己的索引文件。用户将指定每个日志文件应该有多大,以及每个记录必须保留多长时间。
但是如何给这些文件添加名字呢?或许有些随机性?不,不太适合突破。但是由于 1) 名字需要唯一, 2) 每个偏移量都是唯一的,并且 3) 每个文件都将负责一个偏移量范围,那么用两部分来命名这些文件怎么样,其中第一个偏移量的编号以及它所扮演的角色?
例如,假设用户希望每个文件有三个偏移量,而你有五个记录,你会有 0.index 、 0.log 和 0.timeindex 从偏移量 0* 开始并包含前三个记录,以及 3.index 、 3.log 和 3.timeindex 从偏移量 3 开始在第六条记录到达之前,从 3 开始的序列将处于活动状态,因为它还可以保存一条记录。但是当这个新记录到达时,将开始一个新的序列, 6.index 、 6.log 和 6.timeindex ,从偏移量 6 开始并准备好接收接下来的三个记录。*
在停下来思考这一切有多棒之后,你也会意识到将这些文件命名为序列并不是最好的选择,因为术语序列与整个事物(整个大毒蛇)联系更紧密。相反,这些文件是序列的段。漂亮!
美丽简单(作者图片)
现在你有了这些碎片,是时候决定何时和如何吃掉它们了。当很容易。您已经从用户那里收到了每条记录的预期持续时间,并且知道它是何时插入的。时间索引来救援)。只要计算一下,找出每次应该删除哪些记录。
另一方面,删除实际上不是删除,而是分配给每个记录的一种标签。当读者要求数据时,您只需保留这些标签并跳过与之相关的偏移量。下面是大毒蛇的真正贡献:从时间角度来看,属于同一片段的记录很可能是在彼此更接近的情况下生成的。因此,一旦属于一个段的最后一条记录被标记为删除,整个段就可以被删除,这导致了极其高效的删除和零空间浪费!**
大毒蛇,自古埃及以来推动创新。
收获荣誉的时候到了
你已经取得了巨大的成就。您部署了您的新解决方案,它像魔法一样有效。你要打电话给你的朋友,但他先打给你。你甚至没有给他说话的时间,就马上开始拍摄:
伙计,这就是我们所拥有的
1。 我们可以接收包含任何格式的记录并无缝存储它们
2。 我们可以几乎实时地按到达顺序消费这些记录
3。 我们可以非常有效地找到任何特定偏移或时间的记录
4。 我们可以以一种最佳的方式删除记录
是的,我们可以!
你朋友的反应在意料之中。他向您表示祝贺,并说他已经注意到了进展,因为他的模型正在接受测量的狂轰滥炸,没有停止。他还说他打电话给你是想告诉你一些更好的消息。他的模型和你的解决方案做得非常好,现在整个州的气象部门都感兴趣了,其他州的一些城市也开始联系。他们热衷于投资任何需要的基础设施。
虽然你对自己的解决方案很兴奋,但还是让你大吃一惊。从设计的角度来看,您的解决方案很棒,但是它的可扩展性足以支持需求的巨大增长吗?如果您为每个新城市都准备了基础设施,那么如何支持多个城市呢?如何支持多个读者和作者?您如何保证耐用性,也就是说,如果磁盘出现故障怎么办?如何在编写器和读取器全速运行时执行备份?
你和你的朋友分享这些担忧,但是在这一点上没有回头路。他信任你,你的绰号是挑战。但是你累了,在回到那些疯狂的时间之前,你需要一个短暂的休假。这将帮助您考虑如何解决这些可伸缩性问题。你的朋友同意了。
你买了一张去阿尔卑斯山的票,因为当你从高处俯瞰时,你可以更好地思考。你承诺自己会带着改变世界的解决方案回来。
进一步阅读
卡夫卡文献:https://kafka.apache.org/documentation/#uses
卡夫卡 vs 其他 db:https://dzone . com/articles/is-Apache-Kafka-a-database-the-2020-update
页面缓存:https://en.wikipedia.org/wiki/Page_cache
此外,如果你喜欢这种解释,并且对 Spark 感兴趣,你可能会喜欢查看https://towardsdatascience . com/understand-Spark-as-if-you-has-designed-it-c 9c 13 db 6 AC 4b
了解卡普兰-迈耶估计量(生存分析)
里面的艾
一种生存分析技术的介绍。
什么是生存分析?
它是一组用于数据分析的统计操作,感兴趣的结果变量是事件发生前的 时间 。这些事件可能是死亡、疾病发病率、客户流失、恢复等。
它被用来估计特定人群的寿命。
它也被称为 【到事件的时间】 分析,因为目标是估计个人或一组个人经历感兴趣的事件的时间。当时间是一个重要因素时,生存分析用于比较组。其他测试,如简单的线性回归,可以比较组,但这些方法没有时间因素。它集中在两个重要的信息部分,第一个 , 参与者在研究期间是否遭受利益事件;第二个、每个被随访者的随访时间。
生存分析由以下部分组成:
- 生存数据
- 生存功能
- 分析方法
卡普兰-迈耶估计量到底是什么?
Kaplan-Meier 分析测量从某个日期到死亡、失败或其他重大事件的存活时间。也称为乘积限估计量,是一种非参数统计量,用于从寿命数据中估计生存函数。
例如,它可用于计算:
- 失业后人们会失业多久?
- 接受生育治疗的夫妇需要多久才能怀孕?
- 机器零件的无故障时间。
- 治疗后存活时间。(在医学实践中)
Kaplan Meier 估计值的图表是一系列递减的水平步长,在给定足够大的样本量的情况下,它接近该总体的真实生存函数。卡普兰-迈耶估计经常被使用,因为它的假设易于使用。
通过示例了解
例如,我们将调查世界各地政治领袖的一生。在这种情况下,政治领袖是由控制统治政权的个人在任时间来定义的。出生事件是个人任期的开始,死亡事件是个人的退休。
审查可能发生,如果他们是,
- 在汇编数据集时仍在办公室(2008 年)
- 在位时死亡(包括暗杀)。
考虑以下数据(前 20 个观察值,来自 1808 个观察值),
为了估计生存函数,我们首先将使用卡普兰-迈耶估计,定义为:
其中‘d’是时间‘t’时死亡事件的数量,而‘n’是时间‘t’之前处于死亡风险中的受试者的数量。
生存函数
上图显示了政治领导人使用 Kaplar-Meier 估计值的生存函数。y 轴代表领导者在“t”年后仍然存在的概率,其中“t”年在 x 轴上。我们看到很少有领导人在位超过 20 年。
此外,我们还可以将数据分为不同的政权,如下图所示。
全球制度
令人难以置信的是,这些非民主政权还能存在多久!我们也可以通过下面的国与国的比较来理解,
重要!
在进行卡普兰-迈耶分析时,为了避免常见错误,可以记住以下几点:
- 为了对这些生存概率做出推断,我们需要对数秩检验。
- 将变量分成两类,以便将值分类为低或高。中值分界点通常用于区分高低分组,以避免像对数秩检验只比较组间存活率这样的问题。
- 卡普兰迈耶是一个单变量的方法。这意味着 Kaplan Meier 的结果很容易有偏差,夸大了预后的重要性,或者完全错过了信号。
- 人们应该研究新的预后因素的附加值,量化新的标志物改善预测的程度。
结论
Kaplan-Meier 估计因其简单和易于使用而被广泛使用。但是在实施时应该小心,因为它可能会导致错误的结果,错误的假设。
参考
- https://lifelines.readthedocs.io/en/latest/Quickstart.html
- https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3932959/
- 【https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3059453/
- 完整的 Python 分析,
机器学习和深度学习项目。为 pr2tik1/ml-models 开发贡献一份力量
github.com](https://github.com/pr2tik1/ml-models/blob/master/survival-analysis/kaplan-meier.ipynb)
其他职位:
根据调查数据分析开发人员和编程趋势。
pr2tik1.medium.com](https://pr2tik1.medium.com/what-happens-to-programmers-in-2020-d04a6bd7452f) [## 探索神经网络(第一部分)
理解深度学习的概念以及使用 Python 和它的神经网络的实现…
medium.com](https://medium.com/towards-artificial-intelligence/neural-networks-from-scratch-a-brief-introduction-for-beginners-d3776599aaac) [## 如何创建令人敬畏的 Github 个人资料-自述文件!
探索展示你作为开发者或开源贡献者的“GitHub 简历”的新方法。每一个开源…
towardsdatascience.com](/explore-new-github-readme-feature-7d5cc21bf02f)
理解机器学习中的潜在空间
学习深度学习的一个基本的,但经常是“隐藏的”概念
来源:Hackernoon,https://hacker noon . com/latent-space-visualization-deep-learning-bits-2-bd09a 46920 df
什么是潜在空间?
如果我必须用一句话来描述潜在空间,它仅仅意味着压缩数据的表示。
想象一个大型的手写数字(0–9)数据集,如上图所示。与其他不同数字的图像(即 3 对 7)相比,相同数字的手写图像(即 3 的图像)彼此最相似。但是我们能训练一个算法来识别这些相似之处吗?如何?
如果你训练一个模型对数字进行分类,那么也训练这个模型学习图像之间的“结构相似性”。事实上,这就是模型首先能够对数字进行分类的方式——通过学习每个数字的特征。
如果看起来这个过程对你是“隐藏的”,那是因为它确实是。潜伏,顾名思义,就是“隐藏”的意思
“潜在空间”的概念是重要的,因为它的效用是“深度学习”的核心——学习数据的特征并简化数据表示,以找到模式。
好奇吗?让我们一点一点地打破潜在空间:
我们为什么要用 ML 压缩数据?
数据压缩定义为使用比原始表示更少的比特对信息进行编码的过程。这就像取一个 19D 的数据点(需要 19 个值来定义唯一的点)并将所有信息压缩到一个 9D 的数据点中。
压缩的图解。来源:浮士德 2013
通常情况下,数据在机器学习中被压缩,以学习关于数据点的重要信息。我举个例子解释一下。
假设我们想要训练一个模型来使用完全卷积神经网络(FCN)对图像进行分类。(即输出给定图像的数字位数)。当模型“学习”时,它只是学习每一层(边缘、角度等)的特征。)并将特征的组合归因于特定的输出。
但是每次模型通过数据点学习时,图像的维度在最终增加之前首先减少。(参见下面的编码器和瓶颈)。当维数减少时,我们认为这是一种有损压缩。
卷积神经网络的描述。来源:来源:Hackernoon 潜在空间可视化。
因为要求模型然后重建压缩数据(见解码器),它必须学会存储所有相关信息并忽略噪声。这就是压缩的价值——它允许我们去掉任何无关的信息,只关注最重要的特征。
这种“压缩状态”是我们数据的潜在空间表示。
我说的空间是什么意思?
你可能想知道为什么我们称它为潜在空间。毕竟,乍一看,压缩数据可能不会引起任何形式的“空间”
但这里有个相似之处。
在这个相当简单的例子中,假设我们的原始数据集是尺寸为 5 x 5 x 1 的图像。我们将我们的潜在空间维度设置为 3×1,这意味着我们的压缩数据点是一个三维向量。
示例 5x5x1 数据
“潜在空间”中的压缩 3x1 数据示例
现在,每个压缩数据点仅由 3 个数字唯一定义。这意味着我们可以在 3D 平面上绘制这些数据(一个数字是 x,另一个是 y,另一个是 z)。
在 3D 空间中绘制的点(0.4,0.3,0.8)
这就是我们所说的“空间”。
每当我们在潜在空间中绘制点或思考点时,我们可以将它们想象为空间中的坐标,在该坐标中,【相似】的点在图上靠得更近。
一个自然出现的问题是,我们如何想象 4D 点或 n 维点的空间,或者甚至是非向量的空间(因为潜在空间表示不需要是 2 维或 3 维向量,并且经常不是,因为太多的信息将丢失)。
不满意的答案是,我们不能。我们是三维生物,无法理解 n 维空间(比如 n > 3)。然而,有一些工具,比如 t-SNE,可以将我们更高维度的潜在空间表征转化为我们能够可视化的表征(2D 或 3D)。(参见下面的可视化潜在空间部分。)
但是你可能想知道,什么是“相似”图像,为什么减少我们数据的维度会使相似的图像在空间上“更接近”?
我说的相似是什么意思?
如果我们看三张图片,两张椅子和一张桌子,我们会很容易地说这两张椅子图片最相似,而桌子与这两张椅子图片最不同。
两把椅子和一张桌子。
但是是什么让这两张椅子的图像“更相似呢?”椅子具有可区分的特征(即靠背、无抽屉、腿间连接)。我们的模型可以通过学习边缘、角度等模式来“理解”这些。
如前所述,这些特征被打包在数据的潜在空间表示中。
因此,随着维度的减少,对每个图像(即椅子颜色)不同的“无关”信息从我们的潜在空间表示中“移除”,因为只有每个图像的最重要的特征存储在潜在空间表示中。**
结果,当我们降低维度时,两把椅子的表现变得不那么明显,而更相似。如果我们在太空中想象它们,它们会离得更近。
*请注意,我在整篇文章中提到的“接近度”是一个模糊的术语,不是一个确定的欧几里德距离,因为空间距离有多种定义。
为什么潜在空间很重要?
潜在空间的概念绝对是耐人寻味。但是怎么用呢?我们什么时候使用它?而且最重要的是,为什么是?
我们会发现,潜在空间“隐藏”在许多我们最喜欢的图像处理网络、生成模型等中。
虽然潜在空间对大多数人来说是隐藏的,但是在某些任务中,了解潜在空间不仅是有益的,而且是必要的。
表征学习
数据的潜在空间表示包含了表示原始数据点所需的所有重要信息。
该表示法必须表示原始数据的特征。
换句话说,模型学习数据特征并简化其表示,使其更容易分析。
这是一个名为表示学习的概念的核心,定义为一套技术,允许系统从原始数据中发现特征检测或分类所需的表示。**
在这个用例中,我们的潜在空间表示用于将更复杂形式的原始数据(即图像、视频)转换为“更便于处理”和分析的更简单表示。
下面列出的是表征学习的具体例子。
多支管
潜在空间是表征学习的子领域流形学习中的一个基本概念。
数据科学中的流形可以理解为在某些方面“相似”的数据组或子集。
这些相似性,通常在高维空间中不易察觉或模糊不清,一旦我们的数据在潜在空间中被表示出来,就可以被发现。
以下面的“瑞士卷”为例。
***
瑞士卷的 3D 表示与相同数据的 2D 表示。来自https://datascience.stackexchange.com/a/5698的例子*
在 3D 中,我们知道存在组类似的数据点,但是用更高维度的数据来描绘这样的组要困难得多。**
通过将我们的数据维度减少到 2D,在这种情况下可以认为是一种“潜在空间”表示,我们能够更容易地区分我们数据集中的流形(相似数据的组)。
要了解更多关于流形和流形学习的知识,我推荐以下文章:
流形是在更高维度中表示数据的基本工具。但是什么是流形,它们是如何…
towardsdatascience.com](/manifolds-in-data-science-a-brief-overview-2e9dde9437e5) [## 2.2.流形学习-sci kit-学习 0.22.1 文档
寻找最基本的必需品简单的最基本的必需品忘记你的忧虑和冲突我是说最基本的…
scikit-learn.org](https://scikit-learn.org/stable/modules/manifold.html)
自动编码器和生成模型
一种常见类型的深度学习模型操纵潜在空间中数据的“接近度”,这种模型是自动编码器— 一种充当身份函数的神经网络。换句话说,自动编码器学习输出任何输入的内容。
自动编码器的一般结构
现在,如果你是这个领域的新手,你可能会想,为什么我们需要这样的模型?如果它输出的都是它自己,那似乎就没什么用了…
虽然这个推理是有效的,但是我们并不太关心模型输出什么。我们更关心模型在这个过程中学到了什么。
当我们强迫一个模型成为一个身份函数时,我们是在强迫它将所有数据的相关特征存储在一个压缩的表示中,这样在那个压缩的形式中就有足够的信息,使得模型能够“准确地”重建它。听起来熟悉吗?应该是,因为这个压缩的表象是我们的潜在空间表象(上图中的红色块)。
我们已经看到了如何在潜在空间中更容易地发现模式,因为相似的数据点往往会聚集在一起,但是我们还没有看到如何从这个潜在空间的中对点进行采样,以似乎生成“新”数据。
通过潜在空间插值生成图像。来源:随机噪声向量的潜在空间上的双线性插值。图 20
在上面的例子中,我们可以通过对潜在空间进行插值来生成不同的面部结构,并使用我们的模型解码器将潜在空间表示重建为与我们的原始输入具有相同维度的 2D 图像。
我说的在潜空间上插值是什么意思?
假设我已经将前一部分的椅子图像压缩成以下 2D 向量,[0.4,0.5]和[0.45,0.45]。假设桌子被压缩到[0.6,0.75]。如果我要对潜在空间进行插值,我将在潜在空间中的“椅子”聚类和“桌子”聚类之间的采样点。
我们可以将这些采样的 2D 向量输入模型的解码器,瞧!我们得到的“新”图像看起来像是介于椅子和桌子之间的变体。*new 在引号中,因为这些生成的图像在技术上并不独立于原始数据样本。
下面是潜在空间中两种椅子之间的线性插值示例。
潜在空间插值。来源:Hackernoon 潜在空间可视化。
图像生成仍然是一个活跃的研究领域,潜在空间是一个必须理解的基本概念。有关生成模型的更多用例,以及使用 GAN(生成对抗网络,另一种使用潜在空间表示的生成模型)的潜在空间插值的实践示例,请参见以下文章。
[## 生成性对抗网络(GANs)的 18 个令人印象深刻的应用
生成式对抗网络(GAN)是一种用于生成式建模的神经网络架构。生成…
machinelearningmastery.com](https://machinelearningmastery.com/impressive-applications-of-generative-adversarial-networks/) [## 生成人脸时如何挖掘 GAN 潜在空间
如何利用插值和矢量运算探索 GAN 潜在空间?生成性对抗网络,或…
machinelearningmastery.com](https://machinelearningmastery.com/how-to-interpolate-and-perform-vector-arithmetic-with-faces-using-a-generative-adversarial-network/)
可视化潜在空间
关于潜在空间可视化的更多内容,我推荐 Hackernoon 的文章,它提供了一个使用 t-SNE 算法可视化 2D 空间中数字图像之间相似性的实践示例。
特色:插值,t-SNE 投影(有 gif 和例子!)
hackernoon.com](https://hackernoon.com/latent-space-visualization-deep-learning-bits-2-bd09a46920df)
关键要点
- 潜在空间是压缩数据的简单表示,其中相似的数据点在空间上更加靠近。
- 潜在空间对于学习数据特征和寻找用于分析的更简单的数据表示是有用的。
- 我们可以通过分析潜在空间中的数据来了解数据点之间的模式或结构相似性,无论是通过流形、聚类等。
- 我们可以在潜在空间中插入数据,并使用我们模型的解码器来“生成”数据样本。
- 我们可以使用 t-SNE 和 LLE 等算法来可视化潜在空间,这些算法将我们的潜在空间表示转化为 2D 或 3D。
在学习“潜在空间”的时候,我被这个“隐藏”却又重要的概念迷住了。我希望这篇文章揭开了潜在空间表示的神秘面纱,并提供了我作为新手所渴望的对深度学习的“更深入的理解”。*
了解线性回归
数据科学的主力
尽管近年来流行更复杂和更奇特的模型,但线性回归仍然难以击败,因为它具有通用性、稳健性和可解释性。
这是一个简单但功能强大的模型,只需完成工作。如果你想进入定量领域,你需要了解它是如何工作的。
线路拟合
线性回归基本就是直线拟合。它会问问题— “最符合我的数据的直线方程是什么?”好看又简单。
一条线的方程式是:
Y = b0 + b1*X
目标变量 y 是我们试图建模的东西。我们想了解(也就是解释)它的变化。在统计学中,方差是不确定性的度量。这是一种价值在它的平均值附近波动的趋势。
波动性的可视化(图形由作者创建)
左图说明了这一点。绿色变量具有很高的方差,因此在其平均值(圆锥的中心)周围有很大的不确定性圆锥。红色变量具有较低的方差。在对变量一无所知的情况下,当猜测红色变量的值时,我们会对自己不会太离谱的能力更有信心。
但是在数据科学和统计学中,我们通常不担心方差,我们只担心无法解释的方差。也就是说,如果我们能找到一些其他的变量(特征变量,X ),来解释 Y 的大部分变化,那么我们是好的。
有了这样一个特征变量,如果有人问我对 Y 的预测,我只需要查找 X 等于什么,我就可以合理地确定 Y 会是什么。Y 的大部分变化已经通过引入 x 得到了解释。
相关特征有助于解释差异
那么为什么直线拟合能帮助我们解释方差呢?想想这条线的方程代表什么。它定义了 X 和 Y 之间的关系。通过将 X 乘以 b1 并与 b0 (b0 + b1*X)求和,我们可以根据给定的 X 值来预测 Y。
正相关的事物往往会一起移动(作者创建的图形)
当然,这只适用于 X 和 Y 相关的情况(正相关或负相关)。相关性衡量两个事物一起运动的趋势(或者在负相关的情况下彼此相反)。比如人的身高和体重是正相关的。你越高,体重越重。
所以直线拟合允许我们从已知的 X 值来推断未知的 Y 值,这要归功于 X 和 Y 之间的相关性。
我们用线性回归方程中的 X 变量来解释 Y 的方差。通过以这种方式解释方差,我们实际上减少了方差——解释方差是我们不再需要担心的方差。
关键的假设是 X 变量是容易测量的,容易理解的,并且它们本身不是一个谜。 一个常见的建模错误是使用本身无法实时观察到的变量来预测某事。用未来的数据预测未来的模型是不好的。
让我们看看这个过程是如何进行的。当我们从一个 Y 变量开始时,我们所知道的只是它的分布。在下图中,所有的方差(绿色圆锥)都是无法解释的,我们对 Y 的未来值的最佳猜测是它的平均值。
仅 Y 变量(由作者创建的图形)
现在,假设我们发现了一个与 y 正相关的特征变量 X。下图显示了 X 如何帮助解释方差。我们可以根据 Y 的 X 值将 Y 的观测值分成两部分。对于较低的 X 值,Y 的期望值为平均值 1,方差近似为左边的红色圆锥。对于较高的 X 值,Y 的期望值为平均值 2,方差近似为右边的红色圆锥。
特征 X 有助于解释 Y 的一些变化(图形由作者创建)
注意通过这种方式分割,方差(红色圆锥)相对于原始绿色圆锥有所减少。我们对 Y 的新预测,或者是均值 1,或者是均值 2,这取决于 X 是什么,比我们以前的天真预测(Y 的均值)要精确得多。
但是为什么要在两个分段上停下来呢?如果我们画更多的蓝色垂直虚线,将数据分成更多的桶,我们可以解释更多的差异,并产生更精确的预测。
这就是线性回归的作用。实际上,它绘制了大量的垂直线,将数据分成许多微小的片段。这最大化了解释的方差和我们预测的精确度。
线条拟合(作者创建的图形)
普通最小二乘法(OLS)
让我们快速了解一下线性回归如何找到最佳拟合线。虽然有一个解析解,但你可以把它看作一个优化问题。线性回归算法想要:
Find the values for **b0** and **b1**that **minimizes the sum of squared errors.**
误差是数据(黑点)和蓝线之间的距离——在下图中,红色箭头表示误差。我们平方误差,因为它可以是正的,也可以是负的。
OLS 可视化(图形由作者创建)
既然我们知道误差是实际观察值(Y,黑点)和我们的预测值(蓝线)之间的差异,我们可以更详细地写出我们的优化问题:
Find the values for **b0** and **b1**that **minimizes the sum of squared errors.**Where squared error for observation i
= **(Yi - (b0 + b1*Xi))^2**And the sum of squared error is just the sum of
(Yi - (b0 + b1*Xi))^2 for all observations.
为了计算误差平方和,我们取一个黑点和蓝线之间的每一个垂直距离,对它们求平方,然后求和。找到使误差平方和最小的 b0 和 b1 值,就能得到最佳拟合线。
回归参数
参数(b0,b1 等。)、被称为贝塔,这种回归是很重要的。在我们之前的例子中,我们只有一个特征变量。但是假设我们有三个特征变量:
Y = b0 + b1*X1 + b2*X2 + b3*X3
- b0 是 Y 轴和蓝线之间的截距,告诉我们当所有特征变量都为 0 时 Y 的期望值。
- b1 告诉我们,当保持 X2 和 X3 不变时,改变 X1 如何影响 y
- b2 告诉我们,当保持 X1 和 X3 不变时,改变 X2 如何影响 y
- b3 告诉我们,当保持 X1 和 X2 不变时,改变 X3 如何影响 y
解释贝塔系数的一个关键假设是,你可以保持其他特征变量不变。在某些情况下,很难在不改变一个特性的情况下改变另一个特性。在这种情况下,我们希望包括这些交互效应(另一篇文章的主题)。
r——量化解释的差异量
最后,我们想弄清楚我们能够解释多少原始方差(因为目标是解释,因此减少方差)。
我们可以通过计算 R:
R^2 = 1 - (residual sum of squares / total sum of squares)
残差(也称为误差)平方和与我们预测误差的方差(黑点和蓝线之间的垂直距离)成比例。预测误差是实际观测值和我们的模型预测值之间的差异。这是一种无法解释的方差的度量。
总平方和与我们试图解释的(Y 的)总方差成正比——它实际上是 Y 方差公式的分子。
残差平方和与总平方和的比率衡量运行线性回归后未解释的方差的比例。换句话说,它是不确定性的比例,我们不能用线性回归使其消失。
因为 R 是 1 减去该比率,所以它可以被认为是解释的方差的比例:
**R^2**
= 1 - (residual sum of squares / total sum of squares)
= 1 - proportion of variance unexplained
= **proportion of variance explained**Since:
prop. of variance unexplained + prop of variance explained = 1
因此,如果在运行我们的回归后,我们看到 R 为 0.90,这意味着我们在模型中包括的 X 变量有助于解释 y 中观察到的 90%的方差。换句话说,我们成功地解释了我们感兴趣的变量周围的许多不确定性。
在以后的文章中,我们会做一些例子,看看当一些支持线性回归的假设被违反时会发生什么。在那之前,干杯!
如果你总体上喜欢这篇文章和我的写作,请考虑通过我在这里的推荐链接注册 Medium 来支持我的写作。谢谢!
延伸阅读:
当我们试图预测未来时,到底发生了什么
towardsdatascience.com](/the-art-of-forecasting-d2a8806b7aa0) [## 用一个简单的例子理解逻辑回归
逻辑回归是进行分类的基本工具之一。作为未来的数据科学家,我…
towardsdatascience.com](/understanding-logistic-regression-using-a-simple-example-163de52ea900) [## 理解最大似然估计
这是什么?它是用来做什么的?
towardsdatascience.com](/understanding-maximum-likelihood-estimation-mle-7e184d3444bd)
了解线性回归
线性回归背后的数学详细解释
作者图片
假设你想从网上商店买一台新电脑(你最感兴趣的是它有多少内存),你在首页上看到一些 4GB 的电脑售价 100 美元,还有一些 16 GB 的售价 1000 美元。你的预算是 500 美元。因此,你在脑子里估计,根据你目前看到的价格,一台 8 GB 内存的电脑应该在 400 美元左右。这将符合您的预算,并决定购买一台 8 GB 内存的电脑。
这种估计几乎可以在你的大脑中自动发生,而不知道它被称为线性回归,也不需要在你的大脑中明确计算回归方程(在我们的例子中:y = 75x - 200)。
那么,线性回归是什么?
我将尝试简单地回答这个问题:
线性回归就是根据一些已知的量来估计一个未知量的过程(这是回归部分),条件是未知量可以通过只使用标量乘法和加法(这是线性部分)这两种运算从已知量中获得。我们将每个已知量乘以某个数,然后将所有这些项相加,得到未知量的估计值。
当它以正式的数学方式或代码描述时,它可能看起来有点复杂,但事实上,如上所述的简单估计过程,你可能在听到机器学习之前就已经知道了。只是你不知道这叫线性回归。
现在,让我们深入线性回归背后的数学。
在线性回归中,我们获得未知变量的估计值(用 y 表示;我们的模型的输出)通过计算我们的已知变量(用 xᵢ表示;输入),我们向其添加了偏置项。
其中 n 是我们拥有的数据点的数量。
添加偏差与想象我们有一个始终为 1 的额外输入变量并且只使用权重是一样的。我们将考虑这种情况,使数学符号变得简单一点。
其中 x₀ 永远是 1, w₀ 是我们之前的 b
为了使记法简单一点,我们将从上面的和记法过渡到矩阵记法。上述等式中的加权和相当于所有输入变量的行向量与所有权重的列向量的乘积。那就是:
上面的等式只针对一个数据点。如果我们想一次计算更多数据点的输出,我们可以将输入行连接成一个矩阵,用 X 表示。对于所有这些不同的输入行,权重向量将保持不变,我们将用 w 表示。现在 y 将用于表示具有所有输出的列向量,而不仅仅是单个值。这个新方程的矩阵形式如下:
给定一个输入矩阵 X 和一个权重向量 w ,我们可以使用上面的公式很容易地得到 y 的值。假设输入值是已知的,或者至少是容易获得的。
但问题是:我们如何获得权重向量?
我们将从例子中学习它们。为了学习权重,我们需要一个数据集,其中我们知道 x 和 y 值,基于这些我们将找到权重向量。
如果我们的数据点是定义我们的回归线所需的最小值(比输入数多 1),那么我们可以简单地求解方程(1)得到 w :
我们称这个东西为回归线,但实际上,它是一条只针对 1 个输入的线。对于 2 个输入,它将是一个平面,对于 3 个输入,它将是某种“3D 平面”,等等。
大多数情况下,上述解决方案的要求并不成立。大多数时候,我们的数据点不会完全符合一条直线。我们的回归线周围会有一些随机噪声,我们将无法获得 w 的精确解。然而,我们将尝试为 w 获得的最佳可能解,以使误差最小。
如果等式(1)无解,这意味着 y 不属于 X 的列空间。因此,我们将使用 y 在 X 的列空间上的投影,而不是 y 。这是离 y 最近的向量,也属于 X 的列空间。如果我们将等式两边相乘。(1)通过 X 的转置,我们将得到一个考虑了这个投影的方程。你可以在麻省理工学院的吉尔伯特·斯特朗的讲座中找到更多关于解决这个问题的线性代数方法。
虽然这个解决方案对 X 的限制比我们之前的方案要少,但是在某些情况下还是不行;我们将在下面看到更多关于这个问题的内容。
另一种获得 w 的方法是使用微积分。主要思想是定义一个误差函数,然后用微积分找到最小化这个误差函数的权重。
我们将定义一个函数 f ,它将一个权重向量作为输入,并给出这些权重将在我们的线性回归问题上产生的平方误差。该函数只是查看数据集的每个真实 y 值与回归模型的估计 y 值之间的差异。然后将所有这些差值平方并相加。在矩阵符号中,这个函数可以写成:
如果这个函数有最小值,它应该在临界点之一(梯度 ∇f 为 0 的点)。所以,我们来找临界点。如果你不熟悉矩阵微分,可以看看这篇维基百科的文章。
我们从计算梯度开始:
然后我们将其设为 0,并求解 w :
我们有一个关键点。现在我们应该弄清楚它是最小值还是最大值。为此,我们将计算 Hessian 矩阵并建立函数 f 的凸凹性。
现在,我们可以观察到关于 H 的什么?如果我们取任意实值向量 z 并将其乘以 H 的两边,我们将得到:
因为 f 是一个凸函数,这意味着我们上面找到的 w 的解是一个极小点,这正是我们要找的。
正如你可能注意到的,我们通过使用前面的线性代数方法和这种寻找权重的微积分方法得到了相同的解决方案。我们可以认为它要么是我们用 y 到 X 的列空间上的投影代替 y 时矩阵方程的解,要么是使误差平方和最小的点。
这种解决方案总是有效吗?号码
比平凡解限制少: w = X ⁻ y 其中我们需要 X 为方阵非奇异矩阵,但还是需要一些条件才能成立。我们需要 Xᵀ X 是可逆的,为此 X 需要有完整的列秩;也就是说,它的所有列都是线性独立的。如果我们的行数比列数多,通常就满足了这个条件。但是如果我们的数据例子比输入变量少,这个条件就不可能成立。
这个 X 具有满列秩的要求与 f 的凸性密切相关。如果你看上面那个关于 f 是凸的小证明,你会注意到,如果 X 具有满列秩,那么 X z 不可能是零向量(假设 z ≠ 0 ),这意味着 H 是正定的,因此 f 是严格凸的。如果 f 是严格凸的,那么它只能有一个最小点,这就解释了为什么在这种情况下我们可以有一个封闭形式的解。
另一方面,如果 X 没有满列秩,那么会有一些 z ≠ 0 ,对于这些 X z = 0 ,因此 f 是非严格凸的。这意味着 f 可能没有单一的最小点,但是有一个同样好的最小点的山谷,我们的封闭形式的解决方案不能捕获所有的最小点。视觉上,未满列秩的情况在 3D 中看起来像这样:
来源: GeoGebra
一种即使在这种情况下也能给我们解决方案的方法是随机梯度下降 (SGD)。这是一种迭代方法,从误差函数 f 表面上的随机点开始,然后在每次迭代中,沿着梯度 ∇f 的负方向向谷底前进。
这个方法总会给我们一个结果(即使有时候需要大量迭代才能水落石出);在 X 上不需要任何条件。
此外,为了提高计算效率,它不会一次使用所有数据。我们的数据矩阵 X 被垂直分割成批。在每次迭代中,仅基于一个这样的批次进行更新。
在列秩 X 不全的情况下,解不会唯一;在“最小谷”中的所有点中,SGD 将只给出一个依赖于随机初始化和批次随机化的点。
SGD 是一种更通用的方法,它不仅仅局限于线性回归;它还用于更复杂的机器学习算法,如神经网络。但在最小二乘线性回归的情况下,我们的优势在于,由于误差函数的凸性,SGD 不会陷入局部最小值,而这在神经网络中是常见的情况。当这种方法将达到最小值时,它将是一种全球性的方法。下面是该算法的简要概述:
其中 α 是一个常数,称为学习率。
现在,如果我们插入本文中上面计算的梯度,我们会得到下面专门针对最小二乘线性回归的结果:
暂时就这样了。在接下来的两篇文章中,我还将展示如何使用一些数值库(如 NumPy、TensorFlow 和 PyTorch)实现线性回归。
更好地理解线性回归并提高您的数字技能
towardsdatascience.com](/how-to-implement-linear-regression-with-numpy-172790d2f1bc) [## 如何用 TensorFlow 实现线性回归
通过实施线性回归学习张量流基础知识
medium.com](https://medium.com/towards-artificial-intelligence/how-to-implement-linear-regression-with-tensorflow-406b2cff1ffa) [## 如何用 PyTorch 实现线性回归
通过实现线性回归学习 PyTorch 基础知识
towardsdatascience.com](/how-to-implement-linear-regression-with-pytorch-5737339296a6)
我希望这些信息对你有用,感谢你的阅读!
这篇文章也贴在我自己的网站这里。随便看看吧!
使用奇异值分解理解线性回归
介绍奇异值分解以及如何用它来解决线性回归问题
作者图片
介绍
解释线性回归的博客文章和教育材料非常常见。在大多数情况下,可能是因为大数据和深度学习偏见,这些教育资源中的大多数都采用梯度下降方法来拟合线、平面或超平面以适应高维数据。在本帖中,我们也将讨论如何解决线性回归问题,但是是通过不同的视角。更具体地说,我们将讨论线性代数最基本的应用之一,以及我们如何用它来解决回归问题。是的,我说的是奇异值分解。这种计算工具被用作解决无数问题的基础,包括使用 PCA 进行维度缩减,以及使用线性回归进行统计学习。
线性模型和线性方程组
通过线性代数的视角,回归问题简化为求解形式为 Ax = b 的线性方程组。这里, A 和 b 是已知的, x 是未知的。我们可以把 x 当做我们的模型。换句话说,我们想要为 x 求解系统,因此, x 是将 A 中的观察值与 b 中的测量值相关联的变量。
在这里, A 是一个数据矩阵。我们可以认为和的行代表同一现象的不同实例。它们可以代表提交给医院的单个患者的记录、正在出售的不同房屋的记录或不同人的脸部照片。作为补充,我们可以将矩阵 A 的列视为记录了 A 的行中每个实例的不同特征。在患者医院的例子中,这样的特征可以包括他/她到达医院时的血压,或者患者是否进行过外科手术。
另外,注意矩阵和可能有不同的形状。首先, A 可以是一个方阵。是的,这是非常不可能的(对于我们通常在数据科学中遇到的情况),但在其他方面是可能的。
矩阵和可以具有不同的形状。可以平方。它可以又宽又矮,也可以又高又瘦——图片由作者提供
第二, A 的列数可以多于行数。在这个场景中, A 会有一个又短又宽的形状。最后,(这是数据科学中最常见的情况),矩阵 A 呈现出又高又瘦的矩阵形式,行数比列数多得多。
但是我为什么要关心矩阵 A 的形状呢?
有趣的是, A 的形状将决定线性方程组是否有解,是否有无穷多个解,或者根本没有解。
先说无聊的案子。如果矩阵是平方的(行数等于列数)并且是可逆的,这意味着矩阵 A 具有满秩(所有列线性无关),这就很好地解决了问题。
如果矩阵 A 是平方且可逆的,则方程组有解——作者图片
然而,如果矩阵的列数比行数多,我们可能会遇到有无穷多个解的情况。为了形象化这个奇怪的场景,想象一个 3 × 6 的矩阵,即 3 行 6 列。我们可以认为它有一个 3D 空间和 6 个不同的向量,我们可以用它们来跨越 3D 空间。然而,要跨越一个 3D 空间,我们只需要 3 个线性无关的向量,但我们有 6 个!这留下了 3 个相关向量,可用于制定无限多的解决方案。
最后,通过类比,如果我们有一个行数比列数多的矩阵 A ,我们可以将它视为试图用比我们需要的更少的向量来跨越一个非常高维的空间。例如,想象一个 6 行 2 列的矩阵。这里,我们有一个 6D 空间,但是我们只有两个向量来跨越它。不管我们怎么努力,在最好的情况下,我们只能在 6D 上跨越一个平面。这是至关重要的,因为如果向量 b 在 A 的列空间中,我们只有 Ax = b 的解。但是在这里, A 的列空间跨越了一个更大的 6D 空间上的 2D 子空间(一个平面)。这使得向量 b 在由 A 的列所跨越的子空间中的概率变得不可能。
为了形象化这种可能性有多大,想象一个 3D 空间和一个由两个向量构成的子空间(一个 3D 平面)。现在,想象你随机选择 3 个值。这将给你一个三维空间的点。现在,问问你自己:我随机选择的点在平面上的概率是多少?
尽管如此,在我们没有一个线性方程组 Ax = b 的解的情况下(或者我们有无穷多个解),我们仍然想尽力而为。为此,我们需要找到最佳近似解。这就是奇异值分解发挥作用的地方。
SVD 的简短介绍
奇异值分解或 SVD 的主要思想是,我们可以将任意形状的矩阵 A 分解成 3 个其他矩阵的乘积。
给定任意形状的矩阵,奇异值分解将 A 分解成 3 个矩阵的乘积: U,σ, Vᵀ — 作者图片
这里, U 是一个 m × m 的方阵,σ是一个形状为 m × n 的矩形矩阵, Vᵀ 是一个形状为 n × n 的方阵。
完整的奇异值分解矩阵——作者图片
矩阵 U 和 Vᵀ 有一个非常特殊的性质。它们是 酉矩阵 。拥有像 U 和 Vᵀ 这样的酉矩阵的一个主要好处是,如果我们将这些矩阵中的一个乘以它的转置(或者反过来),结果等于单位矩阵。
另一方面,矩阵σ是对角的,它存储按相关性排序的非负奇异值。
酉矩阵的性质——作者图片
注意,由于σ矩阵是对角的,只有第一 n 行对角值值得保留。事实上,σ的最后 n 行都是用 0 填充的。为此,通常只保留σ的第一个 r × r 非负对角线值,以及相应的 r 列和 U 和 Vᵀ 行。注意 r = min(m,n) 。这通常被称为经济(或紧凑)SVD,从这一点开始,我们将假设矩阵 U 、σ和 Vᵀ 来自经济过程。
经济奇异值分解数据矩阵—作者图片
值得注意的是,经济 SVD 产生矩阵 U 和σ的形状变化(如果σ的对角线值之一为零, Vᵀ 也会发生形状变化)。如果σ的对角线值都是正的,因此 r = n ,我们丢弃 U 矩阵的右半部分( U 的正交补),这给出了 U 一个矩形 m × r 的形状。更关键的是, U ,可能还有 Vᵀ ,现在都是半酉矩阵,也就是说只有 UᵀU = VᵀV = I 。
奇异值分解提供了一个基础,允许我们根据低秩矩阵近似来重构输入信号。让我说得更清楚些。如果我们将 U 的每一列与 Vᵀ 的相应行组合,并通过相应的 σ 值缩放所得矩阵,我们将得到 A 的最佳秩 1 近似,以最小二乘法表示。
输入矩阵的秩 n 近似(一个图像)一个对于不同的秩-图像由作者提供
并且当我们继续将 U 的列与 Vᵀ 的行组合时,通过相应的 σ 进行缩放,我们得到数据矩阵 A 的下一个最佳秩 I 近似。事实上,这是奇异值分解的另一个优秀应用——数据压缩。但那是另一篇文章的主题。
使用奇异值分解的 U 、σ、和 Vᵀ 矩阵进行图像重建。使用前 256 个奇异值,我们得到原始输入图像的最佳秩-256 近似(它可以在视觉上完美地重建原始图像)-作者图像
正如我们之前说过的,使用非方阵 A 的问题是我们不能求逆。这就是为什么我们不能像解方阵 A 那样解方程组的主要原因。然而,如果我们不能将矩阵 A 求逆,我邀请你问自己以下问题。
什么是最佳矩阵 A⁺ ,当乘以 A 时,会尽可能接近单位矩阵 I ?
这个问题的答案解决了当方程组有无穷多解或无解时,寻找最佳可能解的问题。幸运的是,答案也在 SVD 中。
如果我们知道 SVD 总是存在(对于任何形状的矩阵),并且通过组合 U 的列、 Vᵀ 的行和奇异值 σ ,我们可以几乎完美地重构原始输入矩阵,如果我们试图对 SVD 求逆会发生什么呢?
我就不多说了吧。原来近似解决问题a⁺a≈I 的最佳矩阵 A⁺ 是 SVD 的逆。换句话说,A⁻的最佳近似值是 SVD⁻的最佳近似值。让我们跟着数学走。
通过奇异值分解求 A 的伪逆。伪逆的 A⁺ 是我们能得到的最接近作者不存在的 A⁻ — 的图像
首先,我们计算 A 的奇异值分解,得到矩阵 USVᵀ 。为了求解 x 的方程组,我需要将方程的两边乘以 SVD 矩阵的逆矩阵。幸运的是,现在很容易对 3 个 SVD 矩阵中的每一个求逆。为了求 3 个矩阵 USVᵀ 的乘积的逆,我取逆矩阵的乘积!
在对 USVᵀ 矩阵求逆后,如果我们仔细观察左边,我们可以看到大多数矩阵会疯狂地抵消,给我们留下最佳近似解 x̂ 。注意,由于矩阵 U 是半酉矩阵,只有 UᵀU = I 成立。此外,如果(并且我们假设)所有奇异值都是非负的,那么 V ᵀ 连续成为酉矩阵。因此,为了反转 U 和 Vᵀ ,我们只需将每一个乘以它们的转置,即 UᵀU = I 和 VVᵀ = I 。
找到作者的 b — 图像的投影
如果我们更进一步,将我们的最佳解 x̂ 代入 Ax̂ ,我们将看到大多数矩阵也相互抵消,直到我们到达 UUᵀ 。我们之前说过, U 是半酉矩阵, UUᵀ 不是单位矩阵。相反, UUᵀ 是 b 在 U 的列(因此是 A 的列)所生成的子空间上的投影,这是最小二乘意义上的最佳近似解,即我们找到了最小二乘解 x̂ = 最小值(ax-b‖₂).
注意,如果 A 的列数多于行数,并且有无穷多个解,那么奇异值分解会选择具有最小 2 范数的解,即 x̂ = 最小值(x̂‖₂).
SDV 线性回归
一旦我们建立了所需的 SVD 术语,我们就可以用它来寻找现实世界问题的近似解决方案。在这个例子中,我将使用波士顿房价数据集。房价数据矩阵 A 包含 506 行(代表单个房屋)和 13 列(每一列描述房屋的不同特征)。这 13 项功能包括:
- 按城镇分列的人均犯罪率
- 每个住宅的平均房间数
- 到五个波士顿就业中心的加权距离
你可以在这里看到的完整描述。
我们希望预测中值房价为 $1000。这些测量值是从 5 到 50 的真实值,它们代表我们的方程组 Ax = b 中的 b 向量。
通常,矩阵的行数比列数多得多。这意味着我们不能将 A 求反来找到 Ax = b 的解。此外,它大大降低了找到解决方案的可能性。事实上,只有当 b 是 A 的列的线性组合时,这样的解决方案才是可能的。然而,使用 SVD,我们将能够导出伪逆 A⁺ ,以找到最小二乘法的最佳近似解— ,这是向量 b 到由 a 的列所跨越的子空间上的投影。
代码非常简单,结果非常好。事实上,它们是线性模型的最佳选择。
简单说明一下,请看上面 python 代码的第 9 行。在这一行,我在数据矩阵 A 中添加了一列全 1。该列将允许线性模型学习偏置向量,该偏置向量将向超平面添加偏移,使得它不穿过原点。
看看下面的训练和测试结果。
基于奇异值分解的线性模型的训练预测—图片由作者提供
基于奇异值分解的线性模型的测试预测—图片由作者提供
目标/预测图允许我们直观地评估目标值和模型预测之间的相关性。非常准确的预测使点非常接近虚线-图片由作者提供
感谢阅读!
原载于 2020 年 10 月 12 日https://sthalles . github . io。
理解逻辑回归
这种方法的数学详细解释
作者图片
什么是逻辑回归?逻辑回归只是将线性回归应用于只有 2 个输出的特殊情况:0 或 1。这个东西最常用于分类问题,其中 0 和 1 代表两个不同的类,我们想要区分它们。
线性回归输出一个范围从-∞到+∞的实数。我们甚至可以在 0/1 分类问题中使用它:如果我们得到一个> = 0.5 的值,将其报告为类标签 1,如果输出< 0.5,将其报告为 0。
其中 x 是一个观察值的特征向量(加上偏差为常数 1 的分量), w 是权重向量。
但是,如果我们将回归线压缩成介于 0 和 1 之间的“S”形曲线,我们可以在准确性和可解释性方面获得稍好的结果。我们通过将 sigmoid 函数应用于线性回归模型的输出值来压缩回归线。
下面是 sigmoid 函数:
更确切地说,我们按如下方式计算输出:取输入的加权和,然后将这个结果数传递给 sigmoid 函数,并将 sigmoid 的输出报告为我们的逻辑回归模型的输出。
这一过程有助于我们获得稍好的准确性和输出的可解释性。如果我们的模型输出任何实数,如-5 或 7,这些数字实际上意味着什么?关于我们的两个类:0 和 1,我们能知道什么?
但是,当我们有介于 0 和 1 之间的输出时,我们可以把它们解释为概率。逻辑回归模型的输出是我们的输入属于标有 1 的类别的概率。我们模型输出的补充是我们输入属于标记为 0 的类别的概率。
其中 y 是输入 x 的真实类标签。
好的。至此,我们已经看到了给定输入,逻辑回归模型如何获得输出。但是它的重量呢?它应该有什么样的权重才能做出好的预测?
我们的模型需要学习这些权重,它学习的方式是给我们的模型一个目标函数,然后它找到最小化或最大化这个目标的权重。
有许多方法可以得到一个目标函数,特别是当我们考虑在目标中加入正则项的时候。
在本文中,我们将只探讨两个这样的目标函数。
首先,让我们将逻辑回归模型写成如下形式:
其中 X 是一个矩阵,以行的形式包含我们所有的观察结果,列代表特征。这是我们模型的输出,它是一个向量,包含对每个观察的预测。
让我们以下面的方式重写我们的逻辑回归方程:
最后一行右侧的操作是基于元素的。
你在上面最后一行观察到了什么?如果我们将最后一个等式右侧的函数应用于逻辑回归的标签,并将该函数应用的输出视为新标签,则我们获得线性回归。因此,我们可以使用误差平方和作为损失函数,并找出使其最小的权重。我们可以通过使用封闭形式的公式或 SGD(随机梯度下降)来找到权重,您可以在以下关于线性回归的文章中了解更多信息:
线性回归背后的数学详细解释
towardsdatascience.com](/understanding-linear-regression-eaaaed2d983e)
以下是线性回归的闭合解和损失梯度(我们可以在 SGD 算法中使用):
对于逻辑回归,我们只需将上述两个方程中的 y 替换为前一个方程的右侧:
当我们应用这些公式时,我们为 y hat 提供了真正的标签。
因此,我们可以将逻辑回归视为线性回归的一种形式,并使用线性回归的工具来解决逻辑回归。好的。除此之外我们还能做什么?
我们可以利用逻辑回归的特性来提出一个稍微好一点的方法。逻辑回归有什么类型的输出?一种可能性。
当涉及概率时,一种方便的方法是最大似然估计。我们将找到在给定输入的情况下使标签的可能性最大化的模型的权重。
我们从写似然函数开始。可能性只是给定输入的标签的联合概率,如果我们假设观察是独立的,则可以写成每个观察的概率的乘积。
其中 m 是观察次数。
似然性是一切的函数:输入 x、真实标签 y 和权重 w。但出于我们的目的(相对于 w 最大化它),我们将进一步将其视为 w 的函数。x 和 y 被视为我们无法更改的给定常数。
根据 yi 为 0 或 1,每个个体概率具有下列值之一:
更简洁的写法是:
现在,我们在似然函数中替换这个量,简化它的 argmax,并过渡到矩阵符号:
正如你在上面看到的,最大化权重的可能性和最小化最后一行的数量是一样的。这次找到一个封闭形式的解更加困难(如果可能的话),所以我们能做的最好的事情是计算这个量的梯度:
其中:上述部分中涉及的操作是基于元素的。X 前的点表示“将左边的列向量与矩阵 X 的每一列逐元素相乘”。上面的 1 是与 y 形状相同的列向量,用值 1 填充。
现在,上述梯度可以与基于梯度的优化算法(如 SGD)一起使用,以找到最佳权重。
在结束之前,让我们回顾一下我们在这篇文章中看到的一些东西:
- 什么时候可以用逻辑回归?答:当我们遇到二元分类问题时。
- 逻辑回归模型如何获得其输出?答:它计算其输入的加权和,然后将其传递给 sigmoid 函数。输出可以解释为概率。
- 我们如何找到模型的权重?答:我们可以摆弄标签,这样我们仍然可以使用线性回归,或者我们可以使用更适合它的东西,如 MLE。MLE 倾向于给出稍微好一点的结果。
这就是本文的全部内容。希望你觉得有用。
在接下来的两篇文章中,我将展示如何在 NumPy、TensorFlow 和 PyTorch 中实现逻辑回归。
学习逻辑回归的同时提高你的数字技能
towardsdatascience.com](/how-to-code-logistic-regression-from-scratch-with-numpy-d33c46d08b7f) [## 如何用 TensorFlow 实现 Logistic 回归
…没有你想象的那么难
medium.com](https://medium.com/nabla-squared/how-to-implement-logistic-regression-with-tensorflow-f5bf18416da1) [## 如何用 PyTorch 实现逻辑回归
了解逻辑回归并提高您的 PyTorch 技能
medium.com](https://medium.com/nabla-squared/how-to-implement-logistic-regression-with-pytorch-fe60ea3d7ad)
我希望这些信息对你有用,感谢你的阅读!
这篇文章也贴在我自己的网站这里。随便看看吧!
理解马尔可夫决策过程:强化学习背后的框架
了解强化学习中马尔可夫决策过程的概念
Alexander Schimmeck 在 Unsplash 上的照片
通过 AWS 和 Jakarta 机器学习,以有趣的方式继续我们了解强化学习的旅程。我们已经在第一篇文章中讨论了 AWS Deep Racer 和强化学习(RL)的基础知识。对于没有读过这篇文章或者想重读这篇文章的你,不要着急。您可以在这里再次找到它:
欢迎来到 AWS DeepRacer 之旅
towardsdatascience.com](/aws-deepracer-the-fun-way-of-learning-reinforcement-learning-c961cde9ce8b)
现在,在这篇文章中,我们将讨论如何制定 RL 问题。要做到这一点,我们需要理解马尔可夫决策过程或众所周知的 MDP。
典型的强化学习过程。作者图片
MDP 是一个框架,可以用来制定 RL 问题的数学。几乎所有的 RL 问题都可以建模为具有状态、动作、转移概率和回报函数的 MDP。
那么,我们为什么需要关心 MDP 呢?因为有了 MDP,一个代理人可以得到一个最优的策略,随着时间的推移得到最大的回报,我们将得到最优的结果。
好了,我们开始吧。为了更好地了解 MDP,我们需要先了解 MDP 的组成部分。
马尔可夫性质
未来只取决于现在而不是过去。
这句话概括了马尔可夫性质的原理。另一方面,术语“马尔可夫性质”是指概率论和统计学中随机(或随机确定)过程的无记忆性质。
举个例子,假设你拥有一家餐馆,管理原材料库存。您每周检查库存,并使用结果来订购下周的原材料。这个条件意味着你只考虑本周的库存来预测下周的需求,而不考虑上周的库存水平。
马尔可夫链
马尔可夫链由遵循马尔可夫性质的状态序列组成。这个马尔可夫链实际上是一个概率模型,它依赖于当前状态来预测下一个状态。
为了理解马尔可夫性质和马尔可夫链,我们将以天气预报为例。如果当前状态是多云,那么下一个状态可能是下雨或刮风。在第一个状态到下一个状态的中间,有一个概率我们称之为跃迁概率。
天气链。作者图片
从上图来看,当前状态是阴天的时候,下一个状态 70%会下雨,或者 30%会刮风。如果当前状态是多风的,那么下一个状态有 100%的可能性是多雨的。那么当状态是阴雨的时候,有 80%的概率下一个状态会保持阴雨,有 20%会变成多云。我们可以将这些状态和转移概率显示为如下表格或矩阵:
转移概率表和矩阵
作为总结,我们可以说马尔可夫链由一组状态及其转移概率组成。
马尔可夫奖励过程
马尔可夫奖励过程(MRP)是马尔可夫链的扩展,增加了一个奖励函数。所以,它由状态,转移概率和奖励函数组成。
杰里米·蔡在 Unsplash 上的照片
这个奖励函数给出了我们从每个状态得到的奖励。这个函数将告诉我们在阴天状态下我们获得的奖励,在刮风和下雨状态下我们获得的奖励。这种奖励也可以是正值或负值。
马尔可夫决策过程
至此,我们已经看到了关于马尔可夫性质、马尔可夫链和马尔可夫报酬过程。这些成为马尔可夫决策过程的基础(MDP)。在马尔可夫决策过程中,我们有行动作为马尔可夫奖励过程的补充。
让我们来描述一下这个 MDP,他是一个矿工,他想在格子迷宫中得到一颗钻石。在这种情况下,矿工可以在网格内移动以获取钻石。
钻石猎人迷宫。作者图片
在这种情况下,我们可以描述 MDP 包括:
- 一组状态 s ∈ S 。这些状态代表了世界上所有可能的构型。在这里,我们可以将状态定义为机器人可以移动到的网格。
- 一个转移函数 T(s,a,s’)。这代表了 MDP 的不确定性。给定当前位置,转移函数可以是向下移动 80%,向右移动 5%,向上移动 15%。
- 一个奖励函数 R(s,a,s’)。这个函数显示了每一步获得了多少奖励。代理人的目标是最大化奖励的总和。一般来说,在每一步都会有一个小的负面奖励来鼓励快速解决问题,而在实现目标时会有大的奖励,或者在终止状态或进入像僵尸和火这样的障碍时会有大的负面奖励。
- 一组动作 a ∈ A 。代理可以采取的所有可能操作的集合。在这个例子中,动作是向上、向下、向左和向右。
在 MDP,我们还需要理解几个术语。
行为空间
行动空间是一个行动者为达到目标可以采取的行动。在我们的 miner 示例中,动作空间向上、向下、向左和向右移动。这种行动空间可以分为两种类型。第一个是离散动作空间,第二个是连续动作空间。
政策
在 MDP,代理行为被定义为一种策略。该策略告诉代理在每种状态下要执行的操作。开始时,我们需要初始化一个随机策略。然后,代理将继续学习,最优策略从迭代中产生,其中代理在每个状态中执行良好的动作,这使得累积奖励最大化。
该策略可分为两种类型:
- 确定性策略。给定特定的状态和时间,策略告诉代理执行一个特定的操作。
- 随机政策。这并不直接针对某个特定的动作。相反,它将状态映射到动作空间上的概率分布。
确定性和随机性策略的例子。作者图片
插曲
一个情节是代理从初始状态到最终状态或终止状态所采取的动作。例如,从当前状态进入最终获得钻石的旅程。矿工运动是 D > A > B > C > F > I。
插曲插图。作者图片
返回
在强化学习中,我们的目标是最大化一集的累积回报。这个奖励是代理收到的奖励的总和,而不是代理从当前状态收到的奖励(即时奖励)。这种累积奖励也称为回报。
代理可以通过在每个状态下执行正确的动作来最大化回报。它将在最优策略的指导下实现这一正确的动作。
贴现因素
在奖励最大化的过程中,我们需要考虑当下和未来奖励的重要性。因此,贴现因子开始起作用。这个贴现因子决定了我们对未来奖励和眼前奖励的重视程度。
折扣因子的值范围从 0 到 1。较小的值(接近 0)更重视眼前的回报,而不是未来的回报。另一方面,高价值(接近 1)给予未来回报比眼前回报更大的重要性。
例如,在比赛中,我们的主要目标是完成一圈。那么我们需要给予未来的回报比眼前的回报更重要。所以,我们需要使用一个接近 1 的折现因子。
结论
恭喜你!!
至此,我们已经涵盖了什么是马尔可夫性质、马尔可夫链、马尔可夫报酬过程和马尔可夫决策过程。这包括 MDP 的重要术语,如行动空间、政策、情节、回报和折扣系数。
总之,强化学习可以被表示为具有状态、动作、转移概率和奖励函数的 MDP。
文献学
本课程将介绍智能计算机系统设计的基本思想和技术。一个…
inst.eecs.berkeley.edu](https://inst.eecs.berkeley.edu/~cs188/sp20/) [## 强化学习导论:马尔可夫决策过程
在一个典型的强化学习(RL)问题中,有一个学习者和一个被称为代理的决策者
towardsdatascience.com](/introduction-to-reinforcement-learning-markov-decision-process-44c533ebf8da) [## 马尔可夫性质
在概率论和统计学中,术语马尔可夫性是指随机变量的无记忆性。
en.wikipedia.org](https://en.wikipedia.org/wiki/Markov_property) [## 马尔可夫链
马尔可夫链是描述一系列可能事件的随机模型,其中每个事件的概率…
en.wikipedia.org。](https://en.wikipedia.org/wiki/Markov_chain) [## 马尔可夫决策过程
在数学中,马尔可夫决策过程(MDP)是离散时间随机控制过程。它提供了一个…
en.wikipedia.org](https://en.wikipedia.org/wiki/Markov_decision_process)
关于作者
Bima 是一名数据科学家,他总是渴望扩展自己的知识和技能。他毕业于万隆技术学院和新南威尔士大学,分别是采矿工程师。然后他通过 HardvardX、IBM、Udacity 等的各种在线课程开始了他的数据科学之旅。目前,他正与 DANA Indonesia 一起在印度尼西亚建立一个无现金社会。
如果您有任何疑问或需要讨论的话题,请通过 LinkedIn 联系 Bima。
用代码理解数学符号
当我们用我们最喜欢的语言来看时,求和、阶乘、矩阵等等都很简单
对于任何有兴趣从事机器学习和数据科学职业或研究的人来说,有一天将会到来,那就是超越 python 库,跟随好奇心进入这一切背后的数学。这通常会把你带到一个庞大的、公开的论文集,详细说明它是如何工作的。你对核心数学理解得越深,你可能就越接近创造新方法的灵光一现。在第一张纸上,一切似乎都很好,直到你遇到这样的事情:
对于任何研究数学多年或从事机器学习数学层面工作的人来说,这样的等式可以被仔细解析为含义和代码。然而,对许多其他人来说,这可能看起来像象形文字。事实是,古代数学领袖似乎选择了最有趣的符号来描述相当直观的方法。结果是:方程和变量看起来比实际复杂得多。
我发现代码不仅可以用来写程序,也是一种全球公认的解释复杂事物的语言。当我在学习一切数据科学背后的数学时,我总是发现获得对数学的普遍理解的最好方法是编写代码片段来描述方程。最终,这些符号变得可以理解,几乎可以作为普通报纸上的文本阅读。在这篇文章中,我希望分享一些用代码描述数学是多么简单的例子!
求和与乘积
求和符号是迭代数学中最有用和最常用的符号之一。尽管设计复杂,但实现非常简单,而且非常有用。
x = [1, 2, 3, 4, 5, 6]
result = 0for i in range(6):
result += x[i]Output of print(result) -> 21
如上所述,这个符号所代表的只是一个从底部数字开始,在顶部数字范围内的 for 循环。底部设置的变量成为索引变量,每个循环的任何结果都被添加到一个总值中。不太常见的是,可以使用以下方法:
这个符号通常被称为乘积运算符,其功能相同,但不是将每个结果相加,而是相乘。
x = [1, 2, 3, 4, 5, 1]
result = 1for i in range(6):
result *= x[i]Output of print(result) -> 120
阶乘
阶乘是“!”几乎存在于任何计算器上。对许多人来说,这一点可能更加明显,但编写一些代码来理解其中的机制仍然是值得的。
5!将表示为:
result = 1
for i in range(1,6):
result *= i
Output of print(result) -> 120
条件括号
条件括号用于根据一组条件转移方程式的流向。对于编码人员来说,这只是常见的“如果”语句。上述条件可以表示为:
i = 3
y = [-2, 3, 4, 1]
result = 0if i in y:
result = sum(y)
elif i > 0:
result = 1
else:
result = 0print(result) -> 6
如上所述,括号中每一行的正确符号指示了每条路径应该执行的内容。我在每个条件中添加了额外的“包含”符号,以增加更多的洞察力。如上所述,我们检查了 I 值是否在 y 列表中。认识到这一点后,我们返回了数组的和。如果 I 值不在数组中,我们将根据该值返回 0 或 1。
逐点和笛卡尔矩阵乘法
最后,我想快速介绍一些操作,这些操作通常是任何数据科学家通过他们最喜欢的语言库——矩阵乘法来完成的。最容易理解的形式是逐点操作。这简单地写成:
注意第一个要求是每个矩阵必须有相同的形状(即#行= & #列=)
其代码如下所示:
y = [[2,1],[4,3]]
z = [[1,2],[3,4]]
x = [[0,0],[0,0]]for i in range(len(y)):
for j in range(len(y[0])):
x[i][j] = y[i][j] * z[i][j]print(x) -> [[2, 2], [12, 12]]
最后,让我们看看一个典型的矩阵乘法过程,最常用于机器学习。用复杂的术语来说,这个操作寻找每个主要行与每个次要列的点积。主要是以下要求:假设[#rows,# columns]→矩阵 i x j 需要#columns(i) == #rows(j) →最终乘积的形状为[#rows(i),#columns(j)]
这可能看起来令人困惑,我最好的建议是看看 google images,看看这些需求的一些很好的可视化。
该等式的代码如下所示(使用 numpy 点方法):
y = [[1,2],[3,4]]
z = [[2], [1]]
# x has shape [2, 1]
x = [[0], [0]]for i in range(len(y))
for j in range(len(z):
x[i][j] = np.dot(y[i], z[:, j])
print(x) -> [[4],
[10]]
这只是几个例子,但是对这个简单代码的理解可以让任何一个程序员去了解最初不吉利的数学世界。当然,为了提高效率,这些方法都可以合并,并且通常都有现成的库方法。用简单的代码编写这些代码的目的是为了看看当它们以真实操作的形式写出来时有多大意义。
在https://www.mindbuilderai.com看看我目前在做什么
来源
[1]https://commons . wikimedia . org/wiki/File:Pure-mathematics-formula-blackboard . jpg
理解推荐系统的矩阵分解
了解如何实现 Google 在实现协作过滤模型时使用的矩阵分解算法
介绍
矩阵成了面试问题中必不可少的话题,不管你是开发人员还是数据工程师。作为一名数据工程师,操作和转换矩阵索引是一项非凡的能力。这个想法是在它下面学习数学。当你理解了算法,你需要做的就是玩指数来得到答案。
在这篇文章中,我将一步一步地分享实现矩阵分解函数的算法。代码片段应该在 R 中;但是,您可以用您喜欢的任何语言生成代码。
矩阵分解的现实应用是什么?
矩阵分解在许多应用中是一种重要的方法。有超级计算机可以进行矩阵分解。例如,跟踪航班的雷达利用一种叫做卡尔曼滤波的策略。卡尔曼滤波的核心是矩阵分解活动。当卡尔曼滤波器利用雷达跟踪你的飞行时,它们照亮了条件的直接框架。另一个很好的例子是 Google,它应用矩阵分解来开发一个协作过滤模型。
理解数学
矩阵分解算法的工作原理是将原始矩阵分解成两个矩阵,一个是上面的三角形(U ),另一个是下面的三角形(L)。在本教程中,我将坚持将方阵 A 因式分解为 LU,如下所示。
请注意:为了简单起见,我们不用担心 A 的置换行,可以假设 A 小于 5x5。最后,矩阵入口点(中枢)不等于零。本教程的目标是理解数学,并将其转化为代码。更复杂的版本将很快在另一篇文章中发布。
矩阵 A 是一个 I 乘 j 的正方形矩阵,分解成 L 和 U 矩阵。目标是在矩阵的右上角获得零— (L),在矩阵的左下角获得零— (U)。
U 矩阵
在这种情况下,我们需要找到一种方法来将提到的值降为零。重要的是要明白,我们必须在不交换行的情况下分解矩阵,我们需要保持行的顺序。在这种情况下,我们将使用线性代数中的行缩减方法。
为了更好的理解,我们来举个例子,假设我们有一个维数为 3×3 的方阵,如下图。
为了得到这个矩阵左下角的零,我们要做如下的事情:
我在这里做的是将 4 除以 2——这被称为乘数,然后用它乘以第一行。之后从第二排减去第一排。最后,将结果放在第二行。
对第三行重复这些步骤。注意,第一行应该保持不变。
现在,我们需要将 6 化为 0。我们可以通过实现以下内容来实现这一点:
请注意,我用 R2 而不是 R1,因为我不想失去我在第三行得到的零分。因此,我将该等式交替应用于第二行。
U 的最终形式
L 矩阵
L 矩阵是直接和简单的。这是一个梯队对角矩阵——对角线上是 1,右上角是 0。剩余值将是我们通过行缩减得到的乘数,其中 U 矩阵位于相同的索引中。
因此,L 矩阵的最终值将如下所示:
计算机算法
理解了数学之后,是时候构建一个通用的计算机程序来分解任何维数相对较大的矩阵了。
基本的算法思想
基本思想是遍历矩阵的列,然后遍历每一行来得到乘数。我们从 2 开始 j,因为我们需要保持第一行不变。我们设置了一个条件,只在行比列高的时候执行,因为我们想对角移动。我们一直想保留归约得到的零。
下面是用 R 语言实现的算法。您可以使用您喜欢的任何语言来实现相同的功能。
由于执行了两个嵌套的 for 循环— 引用,该算法的时间复杂度为 O(n2)。
结束语
试着继续解决矩阵挑战,学习数学技巧来战胜这类面试问题。一旦你发现问题,你就能抓住算法模式,这是很有帮助的。这样做会帮助你赢得面试,给面试官留下好印象。
最后,我希望这篇文章能让面试过程中的其他人受益。如果你能用不同的语言或使用不同的算法自己解决这个问题,我将不胜感激。感谢您的阅读,祝您求职顺利。
理解最大似然估计
马库斯·斯皮斯克在 Unsplash 上的照片
这是什么?它是用来做什么的?
我第一次学习 MLE 的时候,我记得我只是在想,“嗯?”这听起来更哲学和理想主义,而不是实际的。但事实证明,MLE 实际上非常实用,是一些广泛使用的数据科学工具(如逻辑回归)的关键组成部分。
让我们来看看 MLE 是如何工作的,以及如何用它来估计逻辑回归模型的贝塔系数。
MLE 是什么?
最简单地说,MLE 是一种估计参数的方法。每次我们拟合统计或机器学习模型时,我们都在估计参数。单变量线性回归有以下等式:
Y = B0 + B1*X
我们拟合该模型的目的是在给定 Y 和 x 的观测值的情况下估计参数 B0 和 B1。我们使用普通最小二乘法(OLS)而不是最大似然法来拟合线性回归模型并估计 B0 和 B1。但是类似于 OLS,MLE 是一种在给定我们观察到的情况下估计模型参数的方法。
MLE 提出问题,“给定我们观察到的数据(我们的样本),最大化观察到的数据发生的可能性的模型参数是什么?”
简单的例子
那是相当多的。让我们用一个简单的例子来说明我们的意思。假设我们有一个有盖的盒子,里面装着未知数量的红球和黑球。如果我们从有替换的盒子里随机选择 10 个球,最后我们得到了 9 个黑色的球和 1 个红色的球,这告诉我们盒子里的球是什么?
假设我们开始相信盒子里有相同数量的红色和黑色的球,观察到我们观察到的现象的概率是多少?
**Probability of drawing 9 black and 1 red (assuming 50% are black):**We can do this 10 possible ways (see picture below).Each of the 10 has probability = 0.5^10 = 0.097%Since there are 10 possible ways, we multiply by 10:Probability of 9 black and 1 red = 10 * 0.097% = **0.977%**
抽取 1 个红球和 9 个黑球的 10 种可能方法
我们也可以用一些代码来证实这一点(比起计算概率,我总是更喜欢模拟):
***In:***import numpy as np# Simulate drawing 10 balls 100000 times to see how frequently
# we get 9
trials = [np.random.binomial(10, 0.5) for i in range(1000000)]
print('Probability = ' + str(round(float(sum([1 for i\
in trials if i==9]))\
/len(trials),5)*100) + '%')***Out:***Probability = **0.972%**
模拟概率与我们计算的概率非常接近(它们并不完全匹配,因为模拟概率有方差)。
所以我们的结论是,假设盒子里有 50%的球是黑色的,那么像我们这样挑出尽可能多的黑球的可能性是非常低的。作为通情达理的人,我们会假设黑色球的百分比一定不是 50%,而是更高。那百分比是多少?
这就是 MLE 的用武之地。回想一下,MLE 是我们估计参数的一种方法。问题中的参数是盒子中黑色球的百分比。
MLE 问这个百分比应该是多少,才能最大化观察到我们观察到的现象的可能性(从盒子里抽出 9 个黑球和 1 个红球)。
我们可以使用蒙特卡罗模拟来探索这一点。下面的代码块遍历一个概率范围(盒子中黑色球的百分比)。对于每种概率,我们模拟抽取 10 个球 100,000 次,以查看我们最终得到 9 个黑色球和 1 个红色球的频率。
# For loop to simulate drawing 10 balls from box 100000 times where
# each loop we try a different value for the percentage of balls
# that are blacksims = 100000black_percent_list = [i/100 for i in range(100)]
prob_of_9 = []# For loop that cycles through different probabilities
for p in black_percent_list:
# Simulate drawing 10 balls 100000 times to see how frequently
# we get 9
trials = [np.random.binomial(10, p) for i in range(sims)]
prob_of_9.append(float(sum([1 for i in trials if i==9]))/len(trials))plt.subplots(figsize=(7,5))
plt.plot(prob_of_9)
plt.xlabel('Percentage Black')
plt.ylabel('Probability of Drawing 9 Black, 1 Red')
plt.tight_layout()
plt.show()
plt.savefig('prob_of_9', dpi=150)
我们以下面的情节结束:
抽取 9 个黑球和 1 个红球的概率
看到那座山峰了吗?这就是我们要找的。抽中 9 个黑球和 1 个红球的概率最大的黑球百分比值是其最大似然估计值— 我们的参数(黑球百分比)的估计值,最符合我们观察到的情况。
因此,MLE 有效地执行了以下操作:
- 写一个概率函数,将我们观察到的概率与我们试图估计的参数联系起来:我们可以将我们的概率写成 P(9 个黑色,1 个红色|黑色百分比= b)——假设盒子中黑色球的百分比等于 b,抽取 9 个黑色球和 1 个红色球的概率
- 然后我们找到使 P 最大化的 b 的值(9 黑 1 红|百分比黑=b) 。
从图片中很难看出,但是最大化观察我们所做的概率的黑色百分比值是 90%。似乎显而易见,对吗?虽然这个结果似乎明显是个错误,但支持 MLE 的潜在拟合方法实际上是非常强大和通用的。
最大似然估计和逻辑回归
现在我们知道了它是什么,让我们看看如何使用 MLE 来拟合一个逻辑回归( )如果你需要一个关于逻辑回归的复习,在这里查看我以前的帖子 ) 。
逻辑回归的输出是类别概率。在我之前关于它的博客中,输出是投篮的概率。但是我们的数据是以 1 和 0 的形式出现的,而不是概率。例如,如果我从不同的距离投篮 10 次,我的 Y 变量,即每次投篮的结果,看起来会像这样(1 代表投篮成功):
y = [0, 1, 0, 1, 1, 1, 0, 1, 1, 0]
而我的 X 变量,每次投篮离篮筐的距离(以英尺为单位),看起来就像:
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
我们如何从 1 和 0 到概率?我们可以把每一次击球看作是一个二项分布的随机变量的结果。关于二项分布的更多内容,请阅读我之前的文章。简单地说,这意味着每一次尝试都是它自己的尝试(就像扔硬币一样),有一些潜在的成功概率。只不过我们不仅仅是在估算一个单一的静态成功概率;相反,我们是根据我们投篮时离篮筐有多远来估计成功的概率。
所以我们可以把我们的问题重新架构成一个条件概率(y =投篮的结果):
P(y |离篮筐的距离)
为了使用 MLE,我们需要一些参数来拟合。在单变量逻辑回归中,这些参数是回归 beta:B0 和 B1。在下面的等式中,Z 是投篮命中率的对数(如果你不知道这是什么意思,这里解释一下)。
Z = B0 + B1*X
你可以把 B0 和 B1 想象成描述距离和投篮概率之间关系的隐藏参数。对于 B0 和 B1 的某些值,射击精度和距离之间可能存在强正相关关系。对其他人来说,它可能是微弱的积极的,甚至是消极的(斯蒂芬库里)。如果 B1 设置为等于 0,则根本没有关系:
不同 B0 和 B1 参数对概率的影响
对于每组 B0 和 B1,我们可以使用蒙特卡罗模拟来计算出观察到数据的概率。我们模拟的概率是对于一组猜测的 B0,B1 值,观察到我们的精确投篮序列(y=[0,1,0,1,1,1,0,1,1,0],给定离篮筐的距离=[1,2,3,4,5,6,7,8,9,10])的概率。
对于给定的 B0 和 B1,P(y=[0,1,0,1,1,1,0,1,0] | Dist=[1,2,3,4,5,6,7,8,9,10])
通过尝试一组不同的值,我们可以找到使 P(y=[0,1,0,1,1,1,0,1,1,0] | Dist=[1,2,3,4,5,6,7,8,9,10]) 最大化的 B0 和 B1 的值。这些将是 B0 和 B1 的最大似然估计。
显然,在逻辑回归和一般的 MLE 中,我们不会进行强力猜测。相反,我们创建了一个成本函数,它基本上是我们试图最大化的概率的倒置形式。这个代价函数与 P(y=[0,1,0,1,1,1,0,1,1,0] | Dist=[1,2,3,4,5,6,7,8,9,10]) 成反比,和它一样,代价函数的值随着我们的参数 B0 和 B1 而变化。我们可以通过使用梯度下降来最小化该成本函数,从而找到 B0 和 B1 的最佳值。
但在精神上,我们对 MLE 一如既往地做的是询问和回答以下问题:
给定我们观察到的数据,使观察到的数据发生的可能性最大化的模型参数是什么?
我在这篇文章中提到了以下文章:
通过一个游戏实例理解 MC 实验。
鸣谢:来自 Dota 2 的截图
我们经常会在项目中遇到不确定性,在这种情况下,我们需要对不同的成功机会进行评估。解决这种情况的一个非常好的方法是使用从潜在的不确定概率中得出的结果的重复模拟。这种技术被称为蒙特卡罗方法(或简称为 MC),它是以摩纳哥的蒙特卡洛赌场命名的,斯坦尼斯瓦夫·乌拉姆的叔叔在那里赌博。斯坦尼斯劳·乌兰和约翰·冯·努曼在秘密的曼哈顿计划中研究了这些方法。
这些方法的目的是为未知的确定性值找到一个数值近似值。这些方法强调在大规模试验中使用重复抽样,并使用 CLT 等工具逼近结果,这些工具将保证样本均值遵循正态分布,而不管随机过程的基本分布如何。
从观察者的角度来看,进行这样的实验大致需要以下步骤:
a.建立一个分布或概率向量,从中得出结果
b.多次重复得出结果的实验
c.如果结果是一个数字标量,则结果的平均值应按照 CLT 正态分布。
d.估计平均值、标准偏差,并构建预期结果的置信区间。
尽管通过阅读上面的内容看起来很简单,但问题的关键是编写适当的函数和代码来完成上面的步骤。如果潜在的随机变量是一个已知的常见分布,或者你需要使用一个随机发生器来选择一个结果,这个问题就简化了。
下面使用游戏 Dota 2 的例子来解释该方法,问题被定义如下:
“在 Dota 2 游戏中,玩家可以选择从宝藏中购买物品。这些物品通常是特定 Dota 2 英雄或另一个 NPC 的皮肤。在每个宝藏中,有两类物品,普通物品和稀有物品。玩家将购买宝藏的钥匙,然后旋转轮盘,轮盘将从常规物品或稀有物品中随机选择一个物品。每个物品的赔率是不相等的,稀有物品有不同的赔率,赔率随着每次旋转而减少,而普通物品的赔率是恒定的。
假设有 9 个常规物品和一个稀有物品,它们的赔率由赔率向量给出。每次旋转的费用是 175 英镑。在第 40 轮,获得稀有物品的几率是 1,这意味着它肯定会以 40 * 175 = 7000 的最高成本被选中。
odds _ to _ get _ rare _ item =[2000.0 583.0 187.0 88.0 51.0 33.0 23.0 17.0 13.1 10.4 8.5 7.1 6.0 5.2 4.5 4.0 3.6 3.2 2 2.9 2.6 2.4 2.2 1 1.9 1.8 1
我们如何估计收到稀有物品的预期成本?"
解决这个问题的方法如下:
a.定义一个模拟实验的函数,如下所示(用 Python 语言):
def simulate_cost_of_getting_rare_item(no_of_items,prob_to_get_rare_item,cost_of_each_round):
这个函数将接受。项目作为输入,赔率向量获得稀有项目,在我们的情况下是一个和每次旋转的成本。这个函数的输出将是所有旋转的总费用,直到找到稀有物品。
b.定义保存累积成本和计算旋转或迭代次数的变量。
cumulative_cost = 0
iteration = 1
然后我们有
c.在最初几次旋转中,必须至少选择一次常规物品,而不能得到一个重复的常规物品,直到所有常规物品都被选择。因此,您可以将模拟逻辑分为两部分,一部分用于初始回合,另一部分用于后续回合。
# for the first few picks; ensure that regular items are picked without repetition. This is achived by restricting the prob_items array with only availabe items subtracting the # already selected items in previous iterations from available items unless rare item is found
while iteration < no_of_items:
cumulative_cost += 175
prob_items = []
从概率向量中取出稀有物品的概率,将其从 1 中减去,并将剩余的概率平均分配给被遗漏的常规物品。根据计算的概率,使用随机方法(来自 random.choices())比较选择的项目。检查在物品概率向量中最后一个位置的是不是稀有物品。
left_out_prob = 1 - prob_to_get_rare_item[iteration-1]
prob_items = [left_out_prob/(no_of_items - iteration) for i in
range(no_of_items-iteration)]
prob_items.append(prob_to_get_rare_item[iteration-1])
item_chosen = choices([i for i in range(no_of_items-
iteration+1)],prob_items)
if item_chosen[0] == len(prob_items)-1:
return iteration, prob_items, item_chosen[0],
cumulative_cost
iteration += 1
d.一旦所有常规物品至少被选择一次,它们将在随后的回合中再次进入战斗(即,如果稀有物品在前 9 回合中没有被选择,则从第 10 回合开始),因此在随后的每次旋转中,要选择的物品数量将保持为 no_of_items。
# once all regular items are picked atleast once, then can repeat
# but since the decreasing odds of rare item, their probabilities
# for any of them getting selected over rare item is minimized
while True:
cumulative_cost += 175
prob_items = []
left_out_prob = 1 - prob_to_get_rare_item[iteration-1]
prob_items = [left_out_prob/(no_of_items - 1) for i in
range(no_of_items-1)]
prob_items.append(prob_to_get_rare_item[iteration-1])
item_chosen = choices([i for i in
range(no_of_items)],prob_items)
if item_chosen[0] == len(prob_items)-1:
return iteration, prob_items, item_chosen[0],
cumulative_cost
iteration += 1
e.函数定义完成后,在 100 * 1000 次试验中重复采样该函数,并计算预期成本
# now perform monte carlo simulations over 100 * 1000 trails
mean_cost = []
for outer_trials in range(100):
cost_vec = []
for inner_trials in range(1000):
iteration, prob_items, pick, cost =
simulate_cost_of_getting_rare_item
(no_of_items,prob_to_get_rare_item,cost_of_each_round)
cost_vec.append(cost)
mean_cost.append(sum(cost_vec)/len(cost_vec))
使用上面的技巧,我们可以估计得到一个稀有物品的预期成本大约是 2250。
azure 笔记本中提供了完整的代码:
[## 微软 Azure 笔记本电脑
提供对运行在微软 Azure 云上的 Jupyter 笔记本的免费在线访问。
notebooks.azure.com](https://notebooks.azure.com/spaturu/projects/dota-cost-of-rare-item)
我写了一个 python 模拟器来帮我玩彩票
我要多长时间才能赢得彩票?
介绍
我在纽约长大,彩票是我家庭文化的重要组成部分。对我来说,一个冰箱看起来是不完整的,除非有一些刮刮乐或百万彩票粘在上面。每个假期或生日,我都会(现在仍然如此)收到刮刮乐彩票,上面还半开玩笑半威胁地写着“如果你赢了大奖,我就能分到一份!”
不管是好是坏,我最终搬到了阿拉巴马州,在那里彩票是非法的。这对我来说有点震惊,但我最终还是习惯了。然而,每当我拜访纽约的家人时,当我看到发光的广告牌时,当我看到每个加油站的原色标志在我脸上炸开时,当我听到我的家人定期在谈话中提起它时,我都会想起它对人们的吸引力。
总而言之,我一直对幕后的实际数字很好奇。我最终决定全力以赴,模拟百万富翁,为我思考这个问题的方式增加一些视角。
目录
- 百万规则复习
- 模拟是如何工作的?
- 3 次模拟运行和可视化的结果
- 结论
百万富翁规则
让我们从基础开始。怎么玩 Mega Millions,有什么规则?(如果您已经知道这一点,请随意跳过)
他们的网站对此解释得很好。下面是一张图片,上面有说明和奖金细目以及相应的赔率。
【https://www.megamillions.com/
花点时间仔细检查一下。是的,在最后一行,赢得百万球的 1/37 的赔率是正确的。这也让我困惑了一分钟。之所以不是 1/25(由于只有 25 个巨型球,你最初会假设是 1/25 ),是因为该奖励是专门针对你只赢得巨型球而没有匹配任何白色球的情况。
模拟是如何工作的?
其核心是 2 个随机数生成器和一个“while-loop”。模拟器继续一次又一次地产生随机数(复制 5 个白球和一个大球),直到它们匹配,也就是赢得头奖。在每次运行期间,都有 if 语句将较小的奖金加起来,这样我们就可以跟踪总的利润/损失。
假设进入:
- 每次抽奖你只需购买一张票(每周 2 张),你总是得到标准的 2 美元的 megaball 票
- 如果你中了头奖,你可以选择一次性现金
- 所有的收益计算都是税前的(在这些情况下,我们也没有考虑货币的时间价值)
见解:
- 将结果与 Mega Millions 网站上的数学概率进行比较很有意思
- 让你有能力说你“中了彩票”
查看下面的代码,或者如果你想了解更多细节,请访问我的 Github
你可能已经注意到了,我把头奖设定为 197,000,000 美元。这不是武断的。我计算了过去 3 年的平均累积奖金。在模拟过程中,您可以随意尝试并改变头奖。
结果
数学上(如果你看了上一节的规则),你应该每 302,575,350 次就中一次头奖。我运行了 3 次模拟器。让我们将这些运行与统计平均值进行比较。
有用的类比:如果你掷骰子 6 次,理论上你应该掷出一个‘6’。然而,我们都知道你可以在第一次尝试中掷出 6 分。或者你可以在 20 次之后掷出 6。彩票中奖也一样。仅仅因为你玩了 3.02 亿次彩票,并不保证你会赢一次。你可能会赢上千次。或者你可能要尝试十亿次才能赢。这就是这些场景有趣之处。你永远不知道会有什么结果。
说了这么多,还是看结果吧!
场景 1 —幸运:2500 万次播放— 244,744 年
如果你每次抽奖(一周两次)买一张彩票,在这种情况下,你需要 244,744 年才能赢得头奖...而这是极其幸运的!请看下面的获奖数量分类:
播放数量:
下面显示了 2500 万次播放中获得的哑弹和奖品类型的分布。
原木检尺
线性标尺
利润/损失:
在你赢得头奖的前一天,你可能会损失 5000 万美元…但是,所有这些年都会有回报,因为一旦你赢了,你将立即领先 1.5 亿美元!(我排除了你在那段时间里从那 5000 万英镑中获得的利息收益…如果你认为你可能不会领先的话)
有趣的是,我们注意到彩票支付的中彩金额最少。
原木
线性的
此场景的中奖票(如果您想在真实场景中试试运气):
场景 2—平均:3.16 亿次播放—3,038,461 年
播放数量:
下面显示了 3.16 亿次播放中获得的哑弹和奖品类型的分布。这是你能得到的统计平均值。如果你每次抽奖(一周两次)都买一张彩票,那么你要花 3,038,461 年才能赢得头奖。这是最有可能的情况,也是你在玩彩票时应该期待的…
原木
线性的
利润/损失:
在你赢得头奖的前一天,你会损失 5.46 亿美元…不幸的是,即使你赢了 300 多万年,你仍然会损失 3.49 亿美元…
原木
线性的
场景 3——不吉利:4.51 亿次播放——434.19 万年
最后,我想我应该给你们展示一下这个不幸的场景,让你们看看这些数字。这些图表看起来与上面的场景几乎相同。
量
利润/损失:
结论
彩票中奖的可能性极小。如果你想要赌博的刺激而没有输钱的头痛,下载这个模拟器@ my GitHub 自己试试。如果你想在真实的事情上测试你的运气,你可以预计它需要大约 300 万年才能赢得头奖(假设你每周买 2 张票)。
但是!如果你决定长期玩下去,并且无论如何都坚持参与,那么在第一个一百万年里,你有大约 1/3 的机会赢得头奖(因此是有利可图的)。如果你在第一个 100 万年中了奖,累积奖金将会超过门票的花费…所以还是有希望的!
【https://andrewhershy.medium.com/membership】考虑通过我的推荐链接加入 Medium:
如果您觉得这很有帮助,请订阅。如果你喜欢我的内容,下面是我做过的一些项目:
使用地理空间数据了解微观移动模式
位置不是关于一个点,而是关于一条线!
根据出行频率给路线涂上颜色。来源:Kepler.gl
我的用户如何在这个城市移动?他们去哪里了?这个城市的“流”是什么样子的?这在一天中是如何变化的?
这些是我们经常从用户(主要是微移动公司或按需公司)那里听到的问题。如果你是一家微型移动公司(拼车、打车、拼车),这些问题也在你的脑海中,这篇文章应该可以帮助你解决其中的一些问题。
什么是移动性分析?
到目前为止,我们已经讨论了主要以两种方式执行地理空间分析:
- 静态点:分析关于不动的静态实体的指标或事件。例如,分析从一个车站出发的车次或这家餐厅的平均准备时间。
要了解更多信息,请查看:
对于一个公司来说,静态位置是他们的业务实体,不会移动久而久之。例如,对于一个…
blog.locale.ai](https://blog.locale.ai/optimizing-the-performance-of-static-locations-geospatially/)
- 聚合:当我们在处理某个地点和时间发生的事件时,使用地理空间聚合系统,如 hexbin 或 geohash。例如,将一个城市的订单聚合到六边形网格上。
#旁注:如果你想了解更多关于网格的内容,请查看:
[## 空间建模花絮:Hexbins vs Geohashes?
如果你是一个像优步一样的两度市场,你会迎合数百万通过你的司机请求搭车的用户…
blog.locale.ai](https://blog.locale.ai/spatial-modelling-tidbits-honeycomb-or-fishnets/)
然而,为了理解行程中两个事件之间的度量,运动分析非常有用。
根据各种参数分析行程
在分析行程时,我们应该关注以下特征:
- 出发地:行程从哪里出发?例如,在早上,当用户前往他们的工作地点时,行程的最后一英里很可能从住宅区或公交车站或地铁站附近开始。
- 目的地:旅程的终点是哪里?同样,在晚上,用户大多会回家。
此起点的目标点。来源:Kepler.gl
- 路径:用户最常走的路线或路径是什么?最有价值的用户走哪些路径?如果有多种方式可以到达一个目的地,用户会选择哪种方式?例如,如果你是一家拼车或拼车公司,这也应该有助于你分析哪些是你最常去的路线,以获得最大数量的用户。
- 距离:出行的平均距离是多少,它是如何随时间变化的?长途旅行从哪里始发?短途到哪里结束?例如,很多去海滩的旅行都是长途的,而且是在周末。此外,它们源自市中心。
- 所用时间:行程的平均时间是多少,随着行程起点的不同,平均时间会有怎样的变化?短途旅行是否也会因为所经过的路线而花费大量的旅行时间?
- 重复性:百分之多少的行程是重复行程?有多少用户是重复用户?
- 旅行可以有几个自己的特点。例如:你从旅行中获得多少收入?他们盈利吗?用户最不安全的路线有哪些?
通过分析运动数据获得的见解
你能从运动分析中获得什么额外的见解?
流动
流量分析(或网络分析)是为了了解城市在一天的不同时间甚至历史上是如何运动的。
例如,对于一家“最后一英里”配送公司,研究流程显示,配送合作伙伴开始向市中心移动,到了晚上,他们开始搬出去,因为他们大多数人住在郊区。但他们需要向内发展,因为这是他们获得订单最多的地区。
我们通常用弧线或线条来表示流动(并利用时间来理解其时间模式)。Arcs 图层有助于我们了解城市中最常去的出发地和目的地以及它们之间的距离。弧线不会显示两点之间的路径。下图显示了奥斯汀市的流量历史分析。
显示水流的 3d 弧线。来源:Kepler.gl
显示整体流程的另一种方式也可以使用线条,这些线条只是弧的视觉表示。
显示航线的线图层。来源:Kepler.gl
顶部 O-D 对
只是从上一点外推,当你研究流量的时候,你也可以识别出哪些是顶级的起止点。了解最常见的起点-目的地对使得重新分配变得很容易。你可以随时让你的车辆(或司机)在这些地点待命。这也应该有助于拼车公司鼓励在这些地点搭车。
闲置点
旅行中有哪些车辆或乘客空闲的地方?对于微移动公司来说,这是一个非常重要的分析。他们的自行车没有被利用的每一分钟都在损失金钱。他们中的大多数都是按每公里收费的。空闲点的一些分析点是:
- 何时:出行中车辆闲置的时间是否有规律可循?例如,下午在热点旅游地点附近。
- 特点:多少辆自行车一起经常出现在那个闲置地点?有多少独特的旅行?有多少独立用户?
- 持续时间:车辆怠速多长时间?比如,在医院外面或者三个小时,直到电影结束。
- 频率:车辆多久怠速一次?有时间或季节的模式吗?
- 到最近车站的距离:如果自行车应该在一个车站,空闲点离那个位置有多远?
军团
出行次数最多的用户是哪些人?你的超级用户有什么共同点?他们平均花多少时间旅行?他们旅行了多远?他们去哪里了?
要了解什么样的人物角色对你的产品和什么样的用例有最大的效用,必须对你的超级用户有深入的了解。
极端值
异常值有助于您了解运动数据中的异常情况。有多少人去市郊?例如,在很多情况下,我们观察到人们跨越国界(这是非法的)。在这种情况下,对一个用户或一次旅行的透彻理解就变得相当有洞察力。
这个用户去了哪里?为什么?他总是走这些路线吗?在这些情况下,拥有一个特定用户或行程的详细视图是很重要的。
电网方面的供需。来源:Kepler.gl
需求-供给
供需分析是优化资产利用的关键(从而减少闲置时间)。
- 下一个需求时间:当旅行正在进行时,目的地的需求是什么样的?如果乘坐的目的地实际上是一个需求不足的地方,你的车辆可能会在那里闲置很长时间,或者乘坐者将不得不返回而没有乘坐。
- 兴趣点:另一个非常有趣的分析是分析运动如何受到不同兴趣点的影响,如学校、大学、商场或旅游目的地。
- 外部事件:分析不同的外部事件,如下雨、大型音乐会、体育比赛、抗议活动如何影响你公司的需求和供应也是很有见地的。
现场运动分析
在场所,我们正在建立一个“运动”分析平台,使用用户、车辆或乘客的地理空间数据。供应和运营团队在微移动中监控现场情况,并做出更多战术决策。产品和分析团队可以使用它来进行历史分析,并做出更具战略性的决策。
为了帮助您更快更好地分析运动,我们已经建立了模型,可以根据您旅行中的 pings 自动创建路径。
我们的使命是为每一家收集位置数据的公司带来与优步和 Grab 相同水平的粒度分析。我们希望按需公司的业务团队能够在一分钟内获得运营洞察力,而不是几个小时或几周之后!
要了解我们的工作,请查看:
“我们在亚马逊的成功取决于我们每年、每月、每周、每天进行多少次实验。”——杰夫…
blog.locale.ai](https://blog.locale.ai/making-location-based-experimentation-a-part-of-our-dna/) [## 使用地理空间数据进行移动分析的产品
是什么让实时位置数据的分析与众不同?
towardsdatascience.com](/a-product-for-movement-analytics-using-geospatial-data-2aa95b18d693)
如果你想获得试玩,可以在 LinkedIn 或Twitter上与我取得联系或者访问我们的 网站 。
原贴 此处 。
理解机器学习中的运动分析
一篇关于运动分析基础知识以及如何利用机器学习来解决这一计算机视觉相关任务的简短文章。
介绍
运动分析是一项简单的任务,你和我每天都在做,不用花太多心思。我们的基本人类生理学已经从我们原始祖先的视觉能力进化到我们今天的感知检测和分析水平。
图片来自http://glenferriessc.com.au/how-good-is-your-posture/
短语“运动分析”是自我描述的;通俗地说,这是对物体如何在环境中移动的理解。
但是让我们用更专业的术语来描述运动分析
运动分析是对物体运动和轨迹的研究。
现在我们对什么是运动分析有了一个概述,但是它用在哪里呢?
运动分析可以应用于各种行业和学科。在医疗相关机构中,运动分析被用作观察行动障碍患者运动的非侵入性方法。
在视频监控中,运动分析用于视频跟踪和面部识别。
人类受试者的步态分析
运动分析是一项总体任务,可以分解为许多部分。
- 物体检测
- 物体定位
- 物体分割
- 跟踪
- 运动检测
- 姿态估计
提到的每一个组件都是一个值得注意的领域,需要另一篇介质文章。一些研究工作已经进入了有效解决每个部分的方法的探索。
我就不赘述了,只是对一些组件进行必要的说明:物体检测、运动检测、和姿态估计。本文将主要讨论它们与运动分析的关系,以及机器学习在其中的应用。
物体/人检测
这里的关键词是'检测'。为了能够理解和进行身体分析,我们首先必须在为分析提供的数字内容(图像序列)中检测身体。
我们来给物体检测下一个合适的定义:
作为计算机视觉任务的对象检测被定义为从图像或序列图像(视频)中的特定类别中识别感兴趣的对象的存在。
对象检测的任务通常包括在感兴趣的对象的实例周围示出边界框,并识别检测到的对象所属的类别。在特定的场景中,我们专注于识别图像中的一个对象;在其他一些情况下,我们对多个检测对象感兴趣。
图片来自 ImageNet 大规模视觉识别挑战赛(https://www . semantic scholar . org/paper/ImageNet-Large-Scale-Visual-Recognition-Challenge-Russakovsky-Deng/e 74 f 9 b 7 f 8 EEC 6 ba 4704 c 206 b 93 BC 8079 af 3d a4 BD)
有几个对象检测应用蓬勃发展的场景示例,例如视觉搜索引擎、人脸检测、动作检测、ariel 图像分析、行人检测和手势识别
通常,使用机器学习技术,有两种实现对象检测的主要方法,第一种是从头开始设计和训练网络架构,包括层的结构和权重参数值的初始化等。
第二种方法是利用 迁移学习 的概念,并在开始对定制数据集进行训练之前,利用在大型数据集上训练的预训练网络。
图片来自https://talentculture.com/
第二种方法消除了第一种方法所伴随的时间过长的缺点,第一种方法是从头开始训练网络所花费的时间要多得多,并且与采用预先训练的网络相比,需要付出更多的努力。
让我们退一步,观察作为计算机视觉任务的物体检测及其这些年的发展。在引入卷积神经网络来解决对象检测之前,传统的机器学习技术被用来识别图像中的对象。
诸如在 2005 年左右引入的【HOG】的方法使用组合 HOG/ SIFT(尺度不变特征变换)来基于图像梯度的归一化局部直方图识别图像内的兴趣点。
结果是生成仿射不变的 HOG 描述符,并在引入了【SVM(支持向量机)】 的检测链中使用,以基于 HOG 描述符检测感兴趣的对象。这项技术对于一般的检测场景(如行人检测)工作得相对较好,并且在与 MIT 行人数据库进行比较时产生了完美的结果。
应当注意,对象检测可以进一步分解为对象分割。对象分割算法和技术产生对象存在的高亮像素作为输出,而不是边界框;下面的链接提供了一个用于对象分割的方法示例:
[## 屏蔽 R-CNN
我们提出了一个概念上简单、灵活、通用的对象实例分割框架。我们的方法…
arxiv.org](https://arxiv.org/abs/1703.06870)
运动检测
关于运动检测的快速说明。
运动检测是对包含运动对象的图像进行图像处理的过程,该图像处理技术能够通过差分方法或背景分割来跟踪运动,其中通过丢弃图像的静止部分以隔离运动部分来提取图像内的运动特征。
这种技术主要依赖于对图像中像素强度的观察来确定该像素是否属于背景类别。通过识别帧或图片之间的像素强度的进展,可以在强度有显著变化的地方推断出运动。
运动检测是运动分析的一个基本方面,因为需要运动检测来确定对什么和在哪里进行分析。
更重要的是,计算机视觉和机器学习技术已经将运动检测的要求从受控环境中的硬件监控设备降低到仅需要摄像机来进行检测和进一步分析的最先进的算法。
姿态估计
姿态估计是从图像或图像序列中导出身体的重要身体部分和关节的位置和方向的过程。
姿态估计的输出是图像内身体姿态配置的 2D 或 3D 刚性表示。用于运动分析的基于姿态估计解决方案的应用的输出通常是由算法生成的图像,该图像以某种形式或方式描绘了原始图像内感兴趣对象的重要身体部位和关节的位置
来自https://github.com/CMU-Perceptual-Computing-Lab/openpose的 Gif
姿态估计已经在计算机视觉中得到发展,并成为一个独立的突出领域。姿态估计与计算机视觉和机器学习中的其他研究领域的关系,例如对象检测、跟踪和运动分析,鼓励了各种解决姿态估计的技术的发展。
我要谈一点技术问题。
为解决姿态估计而设计的早期方法提出了基于组件或实现模块化解决方案的技术。一些解决方案最初从具有静止背景的图像中分离出人的轮廓,然后估计关节和四肢的位置。
其他解决方案提出的技术也是基于阶段的解决方案,其涉及检测和提取视频序列或图像序列中代表人类的 2D 图形,然后基于 2D 图形和轨迹生成 3D 姿态估计。
在我看来,我仅仅触及了这个主题的表面,但至少如果你已经做到了这一步,你已经对运动分析及其子组件有了一些了解。
在以后的文章中,我将深入探讨所提到的运动分析的一些组件,并包括一些代码,说明如何在实际应用中实现它们。
如果你喜欢像这样的信息丰富的文章,请随时关注我,一旦我将来发布类似的文章,你会得到更新。
理解朴素贝叶斯算法
概率分类器
图片由 Riho Kroll 拍摄,Unsplash
朴素贝叶斯是一种分类算法,是基于贝叶斯定理的概率分类器。在进入朴素贝叶斯的复杂性之前,我们首先理解贝叶斯定理。
贝叶斯定理
贝叶斯定理说,如果事件 B 已经发生,那么我们可以求出事件 A 给定 B 的概率
数学上,贝叶斯定理表示为
其中 P(B)!= 0 (!=指不等于)
P(A|B) —事件 A 给定 B 的概率(称为后验)
P(B|A) —事件 B 给定 A 的概率(称为似然)
P(A) —事件 A 的概率(称为先验)
P(B) —事件 B 的概率(称为证据)
例如,如果我们有一个查询点 X (d 维布尔向量),我们必须预测它属于两个类中的哪一个(Y)。给定 X,如何计算 Y 的概率?
上述表示中的问题
想想如果我们有上面的公式,可以有多少种组合。x 是 d 维布尔向量,2^d 向量 x 的可能组合,2 用于输出(2 类分类)。对于 P(X1,X2,…,Xd|Y),总的组合是 2^(d+1) ,这是巨大的,并且在真实世界的场景中是无效的。
图片由 Unsplash 的 Andrea Piacquadio 提供
朴素贝叶斯通过做一个假设来处理这个问题。
朴素贝叶斯
假设 — 输入向量中的每个特征有条件地独立于其他特征。
数学上。
它说明给定 c,A 有条件地独立于 B。
这有什么帮助?
分子,即 P(X1,X2,…,Xd|Y)*P(Y),等价于联合概率模型,可以表示为 P(X1,X2,…,Xd,Y)。然后我们可以用条件概率展开它。
在应用条件独立性的假设时,它可以写成
因此,使用条件独立的概率公式为
优点是,当假设条件独立时,我们只需找到 P(X1,X2,…,Xd|Y)的 (2d+2)值,(与 2^(d+2 相比明显较少) (2^(d+1)和 P(Y)的 2 个值。
如果我们有两个类,给定一个查询点(X),我们可以得到类 1 P(Y=1|X)和类 0 P(Y=0|X)的概率。基于 MAP 判定规则(最大后验概率)来决定分配给查询点的类别,即,给定 X (P(Y|X))时具有较高概率的类别。
例子
根据天气、温度和风力等特征,预测天气条件是否适合打网球。
x =[前景,温度,多风]
Y =[是,否]
作者图片
由于朴素贝叶斯遵循条件独立,我们将为 X 中的所有 I 和 y 中的 j 计算 P(Xi|Yj)
我们将计算可能性表。
表 1
Outlook P(Outlook|class)-其中 Outlook 可以是阴天、雨天和晴天,而 class 可以是是和否
表 2
温度 P(温度|等级)-其中温度可以是热、温和、冷,等级可以是是和否
表 3
Windy P(Windy | class)——其中 Windy 可以是强和弱而 class 可以是是和否
先验知识是:P(类=是)= 9/14
P(类=否)= 5/14
那么如果我们有一个查询点,如何计算它属于哪个类呢?
给定一个查询点 X' =【晴、凉、强】
我们计算 P(class=Yes|X ')和 P(class=No|X ')
p(X’)是一个常数项,它等于
从可能性表中,我们得到公式中所需的值
由于类别'否'给定 X '的概率高于类别'是'给定 X ',查询点属于类别'否'。
优势
- 朴素贝叶斯算法还可以通过比较给定查询点的所有类的概率来执行多类分类。
- nave Bayes 算法在大数据集上是高效的,并且空间复杂度较小。
运行时间复杂度为 O(dc ),其中 d 是查询向量的维数,c 是类别总数。空间复杂度也是 O(dc ),因为我们只存储每个特征相对于类的可能性,即 P(Xd|Yc ),其中 d 是特征,c 是类。所以总的组合是(d*c)。 - 朴素贝叶斯在诸如垃圾邮件过滤和评论分类等文本分类问题上工作得相当好。
有不同类型的 NB,例如高斯朴素贝叶斯,它在处理连续数据时效果很好,多项式朴素贝叶斯,它通过使用文档中每个词的频率来进行文本分类。另一种类型是伯努利朴素贝叶斯,它只在特征采用两个值(0 和 1)时有效,类似于二进制单词袋模型中的值。
结论
朴素贝叶斯是一种分类算法,它基于贝叶斯定理和条件独立性假设。由于时间和空间复杂度较低,它在要求低延迟的实际应用中工作得很好。
感谢阅读!
理解朴素贝叶斯分类器
有多个例子
凯文·Ku 在 Unsplash 上的照片
在的监督学习(分类)的背景下,朴素贝叶斯或者更确切地说贝叶斯学习作为评估其他学习算法的黄金标准,同时作为一种强大的概率建模技术。
在这篇文章中,我们将从概念上讨论朴素贝叶斯分类器的工作原理,以便它以后可以应用于现实世界的数据集。
在许多应用中,属性集和类变量之间的关系是不确定的。换句话说,即使一个测试记录的属性集与一些训练样本相同,它的类标签也不能被确定地预测。这种情况可能是由于有噪声的数据或某些影响分类的混杂因素的存在而出现的。
例如,考虑根据一个人的饮食和锻炼频率来预测这个人是否有心脏病风险的任务。尽管大多数饮食健康、经常锻炼的人患心脏病的几率较小,但由于遗传、过度吸烟和酗酒等其他因素,他们仍有可能患心脏病。确定一个人的饮食是否健康或锻炼频率是否足够也需要解释,这反过来可能会给学习问题带来不确定性。
B 贝叶斯学习是一种为属性集和类变量之间的概率关系建模的方法。为了理解朴素贝叶斯分类器,首先需要理解的是贝叶斯定理。
贝叶斯定理是从贝叶斯定律中推导出来的,它规定:
给定证据 E,事件 H 的概率为:
在哪里,
P(E/H)是事件 E 发生的概率,假设 H 已经发生,
P(H)是事件 H 的先验概率,
P(E)是事件 E 的先验概率
上面的公式及其组成部分看起来非常专业,会让第一次学习它的人感到害怕。为了更好地理解,我们来看一些例子:
示例:电子邮件— 电子邮件总数:100,垃圾邮件:25,无垃圾邮件:75
a) 20 封垃圾邮件包含“购买”一词,5 封垃圾邮件不包含“购买”一词
问】如果一封电子邮件包含“购买”一词,那么它是垃圾邮件的概率有多大?
解答:问题要求我们确定 P(垃圾/购买)。按照上面提到的公式,事件 H 是邮件是‘spam’,事件 E 是它包含了‘buy’这个词。给定示例中的数据,它可以在数学上/概率上写成:
p(垃圾邮件)= 25/100,
p(无垃圾邮件)= 75/100,
p(购买/垃圾邮件)= 20/25,
p(买入)= 25/100
使用贝叶斯定理,P(垃圾邮件/购买)可以计算为:
P(垃圾邮件/购买)= [P(购买/垃圾邮件)* P(垃圾邮件)] / P(购买)
p(垃圾邮件/购买)= [20/25 * 25/100] / 25/100
p(垃圾邮件/购买)= 20/25 = 0.8
b) 15 封垃圾邮件包含“便宜”一词,10 封垃圾邮件不包含“便宜”一词
问】如果一封邮件包含“廉价”一词,那么它是垃圾邮件的概率有多大?
解答:问题要求我们确定 P(垃圾/便宜)。按照上面提到的公式,事件 H 是邮件是‘spam’,事件 E 是它包含了‘廉价’这个词。给定示例中的数据,它可以在数学上/概率上写成:
p(垃圾邮件)= 25/100,
p(无垃圾邮件)= 75/100,
p(便宜/垃圾)= 15/25,
p(便宜)= 25/100
使用贝叶斯定理,P(垃圾邮件/廉价邮件)可以计算为:
P(垃圾邮件/便宜)= [P(便宜/垃圾邮件)* P(垃圾邮件)] / P(便宜)
p(垃圾邮件/便宜)= [15/25 * 25/100] / 25/100
p(垃圾/便宜)= 15/25 = 0.6
c) 12 封垃圾邮件包含“购买&便宜的”字样,0 封垃圾邮件不包含“购买&便宜的”字样
如果一封电子邮件包含“买便宜的”字样,那么它是垃圾邮件的概率有多大?
解答:问题要求我们确定 P(垃圾/买&便宜)。按照上面提到的公式,事件 H 是邮件是‘垃圾邮件’,事件 E 是包含‘便宜买&字样。给定示例中的数据,它可以在数学上/概率上写成:
p(垃圾邮件)= 25/100,
p(无垃圾邮件)= 75/100,
p(买&便宜/垃圾)= 12/25,
p(廉价购买)= 12/100
使用贝叶斯定理,P(垃圾邮件/购买&便宜)可以计算为:
P(垃圾邮件/廉价购买)= [P(廉价购买/垃圾邮件)* P(垃圾邮件)] / P(廉价购买)
p(垃圾邮件/廉价购买)= [12/25 * 25/100] / 12/100
p(垃圾邮件/廉价购买)= 1
它告诉了我们什么?
解决方案:我们得到的信息是,如果电子邮件包含单词“buy”,则该电子邮件是垃圾邮件的概率为 80%。
→这是标准的贝叶斯定理,但是天真因素在哪里呢?在概率中,两个事件 A 和 B 的条件概率写为:
P(A/B) = P(A 交点 B) / P(B)
当我们认为两个事件 A 和 B 是相互独立的时候,天真因素就出现了,这就把上面的符号修改为:
P(A/B) = P(A) * P(B)
如果我们把独立性的天真假设应用到例子的【c)部分,会有什么变化?让我们深入研究并确定。
垃圾邮件:25 封
20 包含“购买”— 20/25
15 包含“便宜”——15/25
→有多少包含“买便宜的”?
P(买&便宜/垃圾)= P(买)* P(便宜)
p(购买便宜的/垃圾邮件)= (20/25) * (15/25) = 12/25
如果是垃圾邮件,包含(计数)“购买便宜”的电子邮件是(12/25) * 25 = 12
无垃圾邮件:75 封邮件
5 包含“购买”— 5/75
10 包含“便宜”——10/75
→有多少包含“买便宜的”?
P(购买&便宜/没有垃圾邮件)= P(购买)* P(便宜)
p(买便宜的/没有垃圾邮件)= (5/75) * (10/75) = 2/225
包含“购买和便宜”的电子邮件是(2/225) * 75 = 2/3
P(买入&便宜)= (12 + (2/3)) / 100 的总概率
现在,电子邮件是垃圾邮件但同时包含“buy & cheap”的概率是多少?
P(垃圾邮件/廉价购买)= [P(廉价购买/垃圾邮件)* P(垃圾邮件)] / P(廉价购买)
p(垃圾邮件/购买和便宜)= [12/25 * 25/100] / (12 + (2/3)) / 100
p(垃圾邮件/购买和便宜)= 18/19 = 0.947
上面的例子在数学上可以写成:
现在,我们已经建立了贝叶斯定理和朴素假设的基础,让我们通过一个例子来看看如何在机器学习中使用它作为分类器:
以下是我们将使用的数据集:
威滕、弗兰克和霍尔的数据挖掘
在这个特定的数据集中,我们总共有 5 个属性。其中 4 个是自变量(天气、温度、湿度、风力),一个是我们将要预测的因变量(玩耍)。这是一个二元分类问题,因为因变量具有布尔性质,包含是或否。
由于这是非确定性的或者更确切地说是概率性的方法,因此该模型没有学习。
我们将对一个实例进行分类
x = <前景=晴朗,温度=凉爽,湿度=高,风=真>
为了计算这个,我们需要目标变量 Play 的先验概率
实例总数为 14,其中 9 个实例的值为 yes ,5 个实例的值为 no 。
p(是)= 9/14
p(否)= 5/14
按照目标变量,自变量的分布可以写成:
为了对实例 x,进行分类,我们需要计算播放=是和播放=否的最大可能性,如下所示:
播放的可能性=是
P(x/是)* P(是)= P(晴/是)* P(凉/是)* P(高/是)* P(真/是)* P(是)
活动的可能性=否
P(x/否)* P(否)= P(晴/否)* P(凉/否)* P(高/否)* P(真/否)* P(否)
由于天真的独立假设,各个属性的概率会成倍增加。
计算上述等式所需的值为:
p(晴天/是)= 2/9
p(酷/是)= 3/9
p(高/是)= 3/9
p(真/是)= 3/9
和
p(晴/否)= 3/5
p(冷/否)= 1/5
p(高/否)= 4/5
p(真/否)= 3/5
P(x/是)* P(是)=(2/9)(3/9)(3/9)(3/9)(9/14)= 0.0053
P(x/否)* P(否)=(3/5)(1/5)(4/5)(3/5)(5/14)= 0.0206
0.0206 > 0.0053
分类—无
朴素贝叶斯的利与弊
赞成者
- 预测一类测试数据集简单快捷。
- 朴素贝叶斯分类器与其他假设独立的模型相比表现更好。
- 与数值数据相比,它在分类数据的情况下表现良好。
- 它需要较少的训练数据。
缺点
- 如果测试数据集中的一个实例有一个在训练期间不存在的类别,那么它将赋予它“零”概率,并且不能进行预测。这就是所谓的零频率问题。
- 它也被认为是一个坏的估计。
- 它仅仅依赖于独立性预测假设。
我将免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
感谢您的阅读。我希望阅读这篇文章的人对朴素贝叶斯有所了解。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
你可以在这里阅读我的更多帖子:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
理解神经网络
对神经网络架构的深入理解
在我之前的文章中,我简要讨论了深度学习以及如何开始使用它。如果你还没有看过那篇文章,请在这里阅读,对深度学习和机器学习有一个直观的认识。
如果你已经看过了,那就开始吧!
感知器
感知器?!你们中的一些人可能已经知道了。无论如何,感知器是神经网络的结构构建模块。就这么简单。通过形成层来组合许多感知机最终成为深度神经网络。感知器架构可能看起来像这样:
感知器模型。来源:关于电路的一切
这里,总共有两层:输入层和输出层。但是,在机器学习的世界中,开发人员不认为输入是一个层,因此他们会说,“这是一个单层感知器模型”。所以,当有人说,“我已经建立了一个 5 层的神经网络”,不要把输入算作一层。那么,这个感知机模型是做什么的呢?如上图所示,我们有 2 个输入和一个带有 sigma 和积分符号的单个节点,然后是输出。该节点计算两个数学表达式以给出输出。首先,它取输入加上一个偏差的加权和,然后该和通过一个非线性激活函数。稍后,激活函数产生预测输出。这整个过程在神经网络中称为前向传播。
请看这张图片:
感知器计算。
正向传播的灵感来自逻辑回归。如果你知道逻辑回归算法,这可能看起来很熟悉,但如果你不知道逻辑回归,这是没有必要的。权重(W)和偏差(b)是由神经网络“训练”的参数,所谓“训练”是指它们被设置为精确值,使得损失最小。
输出(上图中的“y”)是神经网络做出的预测。实际值和预测值之间的差异称为神经网络的损耗。但事情没那么简单。我们取预测值和实际值之间的差,而不是直接差。让我们明白我想说什么。
损失和成本函数
在继续之前,您应该知道的一件事是,预测值计算神经网络的损失,而神经网络是通过计算“Z”来计算的,Z 取决于“W”和“b”。最终,我们可以说损失取决于“W”和“b”。因此,“W”和“b”应设置为损耗最小的值。明确地说,神经网络总是最小化损失,而不是最大化精度。
当解决一个深度学习问题时,数据集是巨大的。例如,假设我们要构建一个图像分类器,对猫和狗的图像进行分类(你可以将其视为“Hello World!”计算机视觉的:)。所以为了训练神经网络,我们需要尽可能多的猫和狗的图像。在机器学习中,一只狗或一只猫的形象被认为是一个“训练例子”。为了训练一个好的神经网络,我们需要大量的训练样本。损失函数是针对单个训练示例计算的损失。所以,实际上我们为神经网络的训练优化的是成本函数。成本函数可以定义为针对每个训练示例单独计算的所有损失的平均值。
让我们假设有“m”个训练例子。那么成本函数就是:
我们把一个神经网络的损耗取为:
损耗(比方说,L) =预测值(比方说,yhat) —实际值(比方说,y)
由于神经网络的损失取决于“W”和“b ”,所以让我们仅针对“W”来绘制上述损失函数(为了简单起见,如果我们将“W”和“b”都考虑在内,我们必须绘制 3d 图,这将很难理解概念。此外,偏差“b”用于将激活函数向左或向右移动,就像公式中的截距线)
图可能看起来像这样:
这将是一条直线
这个损失函数的问题是,它是一条直线,不可能使用优化算法(如梯度下降)来优化损失函数(在下一节中告诉你)。现在,要明白损失函数应该是一条具有全局最小值的曲线。
后来研究人员又想出了另一个损失函数:
这称为“均方误差损失”,用于回归类问题。此外,对于一个训练示例,这似乎是一个公平的等式。它是一条抛物线。但是对于“m”个训练示例,即,对于成本函数,将是具有许多局部最小值的波状曲线。我们想要一个碗状曲线或凸状曲线来优化成本。
有许多局部极小值的波状曲线
所以,后来我们得到了一个公认的损失函数方程:
这个方程被称为“交叉熵损失,它被广泛用于深度学习中的分类问题。
上述等式的成本函数如下所示:
凸形曲线。
现在我们知道了如何计算神经网络的成本,让我们了解如何优化成本函数以获得更好的性能。
反向传播:训练神经网络
反向传播是神经网络构建中最重要的任务。这是对神经网络进行实际训练的过程。这是一项计算量很大的任务。事实上,它是神经网络中整个计算过程的三分之二。在前向传播中,我们看到了如何计算神经网络的成本。在反向传播中,我们使用这个成本来设置“W”和“b”的值,使得它可以最小化神经网络的成本。
一开始,我们将权重(W)和偏差(b)初始化为一些随机的小数字。随着模型的训练,权重和偏差会随着新值而更新。这种更新是在一种叫做梯度下降的优化算法的帮助下完成的。
在数学中,梯度意味着斜率或导数。下降意味着减少。所以通俗地说,梯度下降就是斜率递减。熟悉微积分有助于理解梯度下降。还记得我说过我们需要一个凸形代价函数来进行优化吗?原因是在下降过程中,我们不会陷入局部极小值。如果成本函数是波浪形的,它有机会停留在它的一个局部极小值,我们将永远不会有成本的全局最优值。
梯度下降算法的图形表示。
梯度下降算法的上述表示可以帮助你理解它。
在梯度下降算法中,我们分别计算计算成本相对于权重和偏差的导数。
让我们考虑一个简单的感知器模型,它有两个输入:
计算权重的梯度。
那么权重被更新为:
上式中的α称为神经网络的学习速率。这是一个超参数,稍后我会告诉你更多的超参数。现在,把它当作一个常数。
权重和偏差将被更新,直到成本函数达到其全局最优值。这样我们得到的预测输出误差会更小。
一次正向传播和一次反向传播一起被计为训练的 1 次迭代或时期。深度学习实践者必须在训练模型之前设置历元的数量(另一个超参数)。
深度神经网络
到目前为止,我们一直在研究感知器模型,处理感知器模型非常容易。但是当我们深入网络时,事情就变得很糟糕了。
现实世界中的问题有大量的输入特征,每个输入特征都有自己的权重和偏差,根据问题,模型中有许多隐藏层,每个隐藏层都有许多计算 Z 和 a 的节点。训练过程与我之前描述的相同,但它会重复神经网络中存在的节点数。
深度神经网络的训练。来源: Gfycat
在上面的 GIF 中,你可以猜到一个神经网络的复杂性和实现它的高计算要求。
让我们总结一下到目前为止我们学到的一切:
1。正向传播期间:
-初始化权重和偏差。
-计算每个节点的 Z 和 A。
-计算整个模型的成本。
2。反向传播期间:
-使用梯度下降优化成本。
-分别计算关于权重和偏差的成本梯度。
-更新参数(W & b)
3。重复步骤 1 和 2,直到成本函数达到其全局最优值。
我希望我在整篇文章中表达清楚了,并且你很好地理解了这些概念。如果没有,欢迎在评论中提问。还有,在评论里给你宝贵的建议,我好改进我的文章。
也可以建议你想学习深度学习领域的什么课题。
感谢大家抽出宝贵的时间阅读本文。
理解自然语言处理:人工智能如何理解我们的语言
让我们来一次自然语言处理的进化之旅…
“语言是文化的路线图。它告诉你它的人民来自哪里,他们将去哪里。”
—丽塔·梅·布朗
由 Google AI Research 提出,BI directionEN coderRpresentations fromTtransformers(BERT)是一个SstateofTheArt(SOTA)模型中的NnaturalGoogle 以其先进的算法为我们提供了许多方便而强大的工具。随着自然语言处理领域的前沿研究,谷歌搜索和谷歌翻译是几乎每天都在使用的两大服务,并且几乎成为我们思维的延伸。
机器人看书,图片来自莎士比亚牛津奖学金
在本文中,我们将了解 NLP 的发展以及它是如何变成今天这个样子的。我们将首先浏览机器学习之前的 NLP 简史。之后,我们将深入研究神经网络的进展及其在自然语言处理领域的应用,特别是神经网络中的Re currentNeuralN网络( RNN )。最后,我们将走进 SOTA 的模型,如HATN网络(韩** )** 和BI 方向E**N 编码器 R 代表从 T 变压器( BERT**
自然语言处理简史
“学习另一种语言不仅仅是学习相同事物的不同词汇,而是学习思考事物的另一种方式。”
——弗洛拉·刘易斯
NLP 领域的第一个想法可能早在 17 世纪就有了。笛卡尔和莱布尼茨提出了一种由通用数字代码创建的字典,用于在不同语言之间翻译文本。凯夫·贝克、阿塔纳斯·珂雪和乔安·约阿希姆·贝歇耳随后开发了一种基于逻辑和图像学的明确的通用语言。
1957 年,诺姆·乔姆斯基发表了《句法结构》。这部专著被认为是 20 世纪语言学中最重要的研究之一。该专著用短语结构规则和句法树构建了一个正式的语言结构来分析英语句子。"无色的绿色想法疯狂地沉睡."根据相结构规则构造的名句,语法正确但毫无意义。
“无色的绿色想法疯狂地沉睡”,图片来自 steemit
直到 20 世纪 80 年代,大多数 NLP 系统都是基于复杂的手写规则。直到后来,由于计算能力和可用训练数据的增加,机器学习算法开始发挥作用。最著名的机器学习算法之一是 RNN,一种基于神经网络的架构。随着用机器处理自然语言的需求不断增加,新的模型近年来一直在快速迭代。伯特是现在的 SOTA,也许几年后会被取代,谁知道呢?
我们刚刚复习了英语和西班牙语等拉丁词根语言的历史。其他语言如汉语、印地语和阿拉伯语则完全不同。英语可以用一套简单的规则来描述,而汉语不同,它的语法极其复杂,有时模糊得无法用逻辑元素来定义。
Example of Chinese Paragraphs, Image from 智经研究中心
汉语语法的复杂性和现代机器是基于逻辑电路的事实,也许是为什么流行的编程语言通常是英语的原因。过去曾有过几次用中文编写程序语言的尝试,如易和燕文。这些语言非常类似于我们日常使用的编程语言,如 Basic 和 c。这些尝试并不能证明中文是一种更好的编程语言。
不同词根的语言之间的翻译也更加困难。手语到我们日常语言的翻译也遇到了许多障碍。我猜这就是我们试图建造巴别塔所要付出的代价。为了惩罚试图建造这座塔的人类,据说上帝决定通过让我们说不同的语言来分裂人类。
巴别塔,图片来自 iCR
但随着时间的推移,我们克服了无数的障碍,走到了现在。随着技术的进步,语言障碍越来越不成问题。我们完全可以购买下面视频中的翻译棒,预订我们的日本之旅,而不需要事先了解任何日语。
NLP 算法也在许多其他领域帮助我们,例如自动字幕、用户体验研究、可访问性,甚至这篇文章的写作,因为如果没有语法的帮助,我的英语会很糟糕。现在,让我们深入那些令人敬畏的技术背后的计算机算法。
抽象语法树、上下文无关语法和编译器
"无色的绿色想法疯狂地沉睡."
—诺姆·乔姆斯基
我们现代化的软件产业是建立在自然语言处理的基础上的。语法树的应用之一是我们的编译器。如果没有它,我们将不得不处理机器代码,而不是像 Python 和 JavaScript 这样简单易学的编程语言。想象一下用二进制机器指令来编码我们的机器学习算法,yuck…由诺姆·乔姆斯基发明,AbstractSyntaxTREE(AST和Context-FREEGrammar(CFG)被用来描述和分析我们用来编码的编程语言。
警告:下面的例子是出于教育目的而简化的。如果你真的想知道编译器到底是如何工作的,请参考更专业的文档。
抽象语法树
编译器使用 CFG 来解释以人类可读编程语言编写的代码。它将分解代码的逻辑,并从中解释出递归逻辑。为了理解代码是如何分解成递归逻辑的,最好将流程表示为 AST。下面是从代码中构造出的 AST 的图示。
抽象语法树解释一行代码
上下文无关语法
CFG 用于描述输入语言和输出标记之间的转换规则。在定义编译器的文件中,它通常以如下所示的方式编写:
if_stm : expr = left_bkt cond_stm right_bkt
cond_stm : expr = expr and_stm expr |
expr or_stm expr |
not_stm expr |
num less_than num |
num greater_than num
num : var | const
这些 CFG 基本上定义了 if 和 condition 语句的规则。例如,一个条件语句可以是多个条件语句(“expr”表示其本身,在本例中为“cond_stm”)与 and 语句(“and_stm”,在代码中表示为“&&”),或者是由“>”或“ Wikipedia 等比较器连接的简单数字,更多关于编译器的信息,如著名的另一个编译器(YACC)可以在这里找到。
自然语言处理的神经构建模块:单词嵌入、RNN 和 LSTM
“模仿人脑的复杂性,受神经启发的计算机将以类似于神经元和突触通信的方式工作。它可能会学习或发展记忆。”
—纳耶夫·阿尔·罗德汉
高级 NLP 算法是用各种神经网络构建的。我在我的 Alpha Go 文章中对神经网络有更详细的解释,包括与神经网络相关的历史。神经网络是有向无环图由人工神经元的连接层组成。输入图层中的值逐层传播到输出图层。这些基于神经科学家如何看待我们大脑工作的模型在最近几年显示出了一些竞争性的表现。
人工神经网络的前向传播
单词嵌入
神经网络以及其他机器学习模型通常以数字向量的形式接受输入。但是我们的英语单词不是数字。这就是为什么我们有单词嵌入。单词嵌入指的是将单词和短语从词汇表转换成数字向量的语言建模和特征学习技术。一个例子可能是用神经网络在数千个段落中运行,以收集哪些单词更经常与另一个单词一起出现,从而给它们更接近的值。
一个著名的单词嵌入模型集合可能是 Word2vect。该模型基于浅层神经网络,并假设段落中彼此接近的单词也共享相似的语义值。下面是解释它的视频。
理解 Word2Vec,视频来自 Youtube
递归神经网络(RNN)
RNN 是神经网络的一种变体,最擅长处理顺序数据。声波、股票历史和自然语言等数据被认为是连续的。RNN 是一种神经网络,在处理顺序数据的过程中,输出被反馈到网络中,允许它也考虑过去的状态。
在我们的自然语言中,句子的上下文并不总是由单词的上下文来表示。例如,“其他评论者认为食物很棒,但我认为不好”这句话有两个积极意义的词(棒极了)。人工神经网络对序列没有任何线索,只是总结一切,但 RNN 能够捕捉句子中的倒装句,如“但是”和“不是”,并根据它进行调整。
安正在处理一个句子
RNN 处理了一句话
长短期记忆(LSTM)
"如果不忘记,根本不可能活着。"
― 弗里德里希·尼采
在 RNN 句子处理的例子中,您可能想知道为什么大多数单词都是不相关的。这正是我们需要 LSTM 的原因。LSTM 引入了一个“忘记层”,它决定应该保留或忘记的信息,使模型在大量数据下更容易训练。LSTM 有三种单元状态,即遗忘状态、更新状态和输出状态,每种状态有不同的用途。
忘记状态
在遗忘状态期间,LSTM 将从输入和先前状态中检索信息。然后使用 sigmoid 函数σ来决定是否应该忘记之前的状态。sigmoid 函数将输出 0 到 1 之间的值。通过将它与前一状态的输出相乘,它将决定前一状态的多少应该被遗忘。
LSTM 忘记状态
更新状态
在更新状态期间,LSTM 将尝试更新状态值。一个超正切函数 tanh 将柔和地强制该值位于 0 和 1 之间,这样它就不会累积成一些疯狂的数字。另一个 sigmoid 函数用于确定有多少将被添加到单元状态中。
LSTM 更新状态
输出状态
最后,单元格状态将由另一个超正切函数处理。然后使用一个 sigmoid 函数来确定将有多少输入到输出中。
LSTM 输出状态
更多关于 LSTM 及其数学训练的详细解释可以在这里找到。
文本分类:与韩
恭喜你!我们终于走到了像韩和伯特这样的模式。我们先讨论韩,并不是因为一个模型优于另一个,只是因为韩更容易理解,可能有助于伯特的理解。由大学和微软研究院在 2016 年提出的,韩证明了它在文本分类方面的能力。能够对 Yelp 评论等文本进行分类有助于各种领域,如用户体验研究和支持票证管理。
我们先从说起,它是韩的基石。不要担心,它与我们在上一节中学习的 LSTM 非常相似。在此之后,我们将能够了解韩的建筑。
门控循环单元(GRU)
GRU,图片来自喧闹
“我开玩笑的!虽然是真的。反正过得好。”
— GRU
GRU 代表GateRe currentUnits。与 LSTM 相比,它们的功能较弱,但模型更简单。然而,在某些情况下,会比表现得更好,这一点我们在了解韩的时候会讲得更多。这就是为什么研究人员通常对这两种设备都进行实验,看哪一种效果最好。
GRU 解释道
GRU 通过 sigmoid 函数使用输入和先前状态来做出所有决定。总共有 2 个决定:
- 第一个决定是前一个状态是否将与输入状态合并。
- 第二个决定是这个混合状态(或普通输入状态)或前一个状态是否会进入下一个状态并作为输出。
更多关于 GRU 的变体及其详细的数学模型可以在这里找到。
分层注意网络
在由单词、句子、段落和故事组成的大军中,字母表中的每个字母都是坚定忠诚的战士。一个字母掉了,整个语言都变得含糊不清。”
——维拉·拿撒勒****
传统的 RNN 和 LSTM 很难解释大量的文本,其中一些关键词彼此相距很远。这就是为什么 HAN 的注意机制从上下文向量 u 中生成重要性权重α的原因。这个重要性权重然后被用于选择性地过滤掉值得关注的输出。
韩运用两个主要层次对文本进行分类,即词层和句层。字向量首先被送入由双向 gru 组成的编码器。双向 GRU 就是方向相反的 gru 堆叠在一起。然后,输出用于计算注意力和上下文向量,输出将被输入到句子层的编码器中,以经历类似的过程。最终的输出向量将被相加并馈入 softmax 层。
分层注意网络(韩)解释说
关于韩的详细解释,其数学细节及其在 Yelp 评论上的表现,请参考原文。
语言理解:变压器和伯特
最后,我们做到了如题所示。为了充分理解这一节,我们可能必须理解广泛的数学推理。这些可以在变形金刚和伯特的原始论文中找到。在本文中,我们将只讨论基本架构。
编码器-解码器架构
首先,让我们看看编码器-解码器的架构。在机器翻译中,经常使用编码器-解码器结构,因为源文本和目标文本并不总是一一匹配。编码器首先用于从输入消息产生输出值,解码器将利用该输出值产生输出消息。
利用编码器-解码器架构进行翻译
变压器
“你需要的只是关注”
—变压器原始论文的标题
2017 年,来自谷歌的研究人员提出了一种完全基于自我关注的架构。自我关注意味着模型通过解释输入来自行决定关注,而不是从外部获取关注分数。
有 3 种不同类型的向量,查询向量 Q、密钥向量 K 和值向量 v。这些向量可以被理解为数据搜索机制。
- 查询(Q)是我们寻找的那种信息。
- Key (K)是查询的相关性。
- 值(V)是实际输入。
缩放的点积注意&多头注意,图片来自变形金刚原纸
缩放的点积注意力首先将 Q 和 K 相乘,然后在图中所示的一些转换之后将其与 V 相乘。多头注意力将从缩放的点积注意力中检索到的头连接起来。Mask 用于过滤掉一些值。这个机制解释起来有点复杂,这里的是一篇对注意力机制提供更详细解释的文章。
转换器是基于多头注意力的编码器-解码器架构。由于转换器使用简化的模型,不再考虑像 RNN 这样的位置信息,因此输入嵌入和位置嵌入都应用于输入,以确保模型也捕捉位置信息。添加& Norm 是剩余连接和批量规格化。前馈只是简单的前馈神经网络重塑向量。
变形金刚模型架构,图片来自变形金刚的原文
变压器的双向编码器表示(BERT)
2018 年,来自谷歌的研究人员推出了一种名为 BERT 的语言表示模型。BERT 模型架构是一个多层双向变压器编码器。构建框架有两个步骤——预训练和微调。
- 预训练:在预训练期间,模型在不同的预训练任务中根据未标记的数据进行训练。
- 微调:在微调过程中,首先用预先训练的参数初始化 BERT 模型,然后使用来自下游任务的标记数据微调所有参数。
BERT 预训练和微调程序,图片来自 BERT 的原文
模型在微调后提前了 11 个 NLP 任务的 SOTA。有关 BERT 的模式详情,请参考原始文件。
话说到最后…
我计划写关于自然语言处理和计算机视觉的文章。因为我已经在我的上一篇文章中写了关于卷积神经网络的内容,所以我决定先写一些关于递归神经网络的内容。还有更多关于卷积神经网络的内容,我已经在我的计算机视觉文章中阐述过了。我还打算写关于生成模型和自动化机器学习的文章。人工智能领域有无数的奇迹,跟随我在遥远的未来看到更多!