TowardsDataScience 博客中文翻译 2020(六十一)
过去几十年,数据分析领域发生了巨大的变革。随着存储成本的降低和云计算的采用,指导数据工具设计的限制变得过时,因此—数据工程工具和技术必须发展。我怀疑许多数据团队正在进行复杂的项目,以使他们的数据工程栈现代化,并使用他们所掌握的新技术。许多其他公司正在从零开始设计新的数据生态系统,这些公司正在寻求机器学习和云计算的进步所带来的新商机。pre-Hadoop。
获取数据科学项目想法的指南
如何提出自学、作品集或商业创意?从拥有太多的人那里。
照片由来自 Pexels 的安德里亚·皮亚卡迪奥拍摄
这是学习和展示技能的最佳方式。
但是我经常收到读者的来信,问我“我到底是如何为我的项目想出点子的?”
任何经验丰富的企业家或工程师都会告诉你,他们有太多的想法。但是当你开始的时候并不总是容易的。
以下是我个人的一些想法。
参加社交活动,与人交谈
大多数人出人意料地愿意分享自己的想法。你只需要问一下。
我在社交活动中的默认问题是,“你在做什么或试图解决什么?”
上周在一次虚拟活动中,我与之交谈的每一个非技术人员都分享了他们想要构建的 ML 用例。
现在不要窃取任何人的想法。但是如果你已经花了几个小时学习数据科学,考虑免费帮助别人。你将获得可以写进简历的经验,以及可能对你的职业生涯有用的人脉。
成功人士乐于分享想法。他们明白世界上有无数的问题需要解决,分享不是零和游戏。
利用你的爱好和兴趣产生想法
许多伟大的想法来自于融合不同领域的专业知识。
例如,神经网络的发明者杰弗里·辛顿有心理学背景,他从中汲取了许多关于人工智能的早期想法。
你怎么能把这个应用到你自己的兴趣上呢?
就我个人而言,我喜欢我的狗、羽毛球和烹饪。我也知道机器学习领域的一般话题。因此,我会尝试将一种类型的 ML 与我的每个爱好相匹配,以产生一个想法。
- 我的狗——用机器学习对我的狗的不同吠叫、皱领和咆哮的音频记录进行分类。
- 羽毛球——使用机器学习检测某人挥动羽毛球拍的视频是否有适当的形式。
- 烹饪——按国家对食物图像进行分类。
如果你深入挖掘,这些都可能是非常有趣的项目。
所以问问你自己,你对什么感兴趣?数据科学能帮助你做得更好,或者提取有趣的灵感吗?
解决日常工作中的问题
你现在的工作可能不在数据科学领域。但这并不意味着没有有趣的数据科学问题需要解决。
每个公司都有手动操作任务,渴望实现自动化。如果你自己没有,你营销或客户服务部门的同事可能会有。你能帮助他们吗?
考虑自动化、决策树或数据可视化是否能帮助您组织中的某个人。
如果这超出了您的正常范围,您可能需要在自己的时间内处理它。但是,如果它增加了价值并给你带来了经验,这是一个很小的代价。
当我为一家电子商务公司管理商业智能时,我想进入软件工程领域。因此,我开始在周末编写代码,搜集销售类似产品的竞争对手网站,并自动生成关于我们定价过高的产品的报告。然后我把报告发给我们的采购部门,这样他们就可以降低价格——这个项目帮助我找到了下一份工作。
深入你目前的工作,你几乎可以保证找到一个可以应用数据科学的项目。
熟悉数据科学工具包
即使您不知道每个模型是如何工作的,了解 ML 和数据科学保护伞下的一般主题也是有价值的。
这让你有能力将这些模型应用到你周围的世界。
例如,我知道 NLP 包括“文本分类”、“信息检索”和“问答系统”。
所以当我心中有一个数据集(即:Reddit 线程)时,就很容易想到潜在的应用并产生初步的想法。
一旦你有了高级工具包,想出点子就变得更加容易了。
解决自己的数据科学问题
你在寻找数据科学工作时遇到了什么问题?机器学习能帮助你吗?
也许你可以收集工作公告板,对工作是否与数据科学相关进行分类,并对工作要求进行分析。
这将是一个了不起的项目!
你还可以添加竞争分析,显示公司之间的招聘差异,并展示给你想为之工作的公司。
作为一个雇佣工程师的人,我很想在某人的投资组合中看到这样一个项目的结果。
透过数据科学家眼镜看世界
当你在日常生活中四处走动时,问问你自己什么可以被分析、测试或自动化。
给室内植物浇水:你能分析土壤湿度以优化植物生长吗?
**购物:**百货公司可以用机器学习来检测盗窃吗?
**烹饪:**你冰箱内部的照片能发现哪些食材需要补充吗?
然后选择项目中最小的组件,并实际尝试构建它。
有无数的想法可以偶然发现。你只需要正确的心态去看待它们。
结论
当你刚起步时,想出点子是很难的。我知道是因为我曾经在那里。
但是要明白——所有伟大的想法都来自真实的经历。真空中没有想法。
这就是为什么放下你的笔记本电脑,走出去和人们交谈是很重要的。
经验丰富的企业家有太多的想法,因为他们已经在许多项目上工作,并在不同领域之间交叉传授想法。
最终,你也会有太多想法的时候。到了那里,分享一些吧!
数据科学家 Git 指南
入门
你再也不用害怕 git 了。
在相当长的一段时间里,git 对我来说是一个模糊的、可怕的东西。这有点像拿着一堆精美的瓷器走在绷紧的绳子上。是的,我知道如何做git add
、git commit
和git push
。但是如果我不得不做除此之外的任何事情,我会很快失去平衡,摔下精美的瓷器,git 将不可避免地把我的项目摔成无法辨认的碎片。
如果您是一名开发人员,您可能非常了解 git。但是 git 现在已经成为编程和协作领域的任何人不可或缺的技能,尤其是在数据科学领域。
所以我最终咬紧牙关,试图理解 git 更全面的情况,以及它的命令对我的代码做了什么。幸运的是,事实证明这并不像我想象的那么复杂,通过纠正我潜在的心理模型,我在处理新项目时更有信心,更少焦虑。
git 的四个领域
git 中有四个区域:存储区、工作区、索引和存储库。
作者图片
典型的 git 工作流从左到右工作,从工作区开始。当您对 git 存储库中的文件进行任何更改时,这些更改都会显示在工作区中。要查看哪些文件随您创建的新文件一起更改,只需执行git status
。要显示文件中具体更改了什么的更详细信息,只需执行一个git diff [file name]
。
一旦您对您的更改感到满意,您就可以使用git add
命令将更改后的文件从工作区添加到索引中。
作者图片
索引的功能就像一个临时区域。它之所以存在,是因为您可能正在尝试一些东西,并且在工作区中更改了许多代码,但是您不一定希望所有这些更改都出现在存储区中。
因此,我们的想法是有选择地将您想要的更改添加到索引中,并且您添加的更改最好是一些逻辑单元或相关事物的集合。例如,您可能决定添加所有与您用 Python 编写的新预处理函数相关的文件。
一旦将所有相关文件添加到索引中,最后通过执行git commit -m 'Explanation of my changes'
将它们移动到存储库中。您应该看到现在索引和存储库之间没有区别了——您可以用git diff --cached
来验证这一点。
作者图片
犯了错误
但是生活并不那么理想。最终你会用一个你想改变的不恰当的信息做出承诺,或者你会决定你添加的所有新东西完全破坏了一切,现在你想回到以前的样子。曾经想要将同事的更新包含到您正在处理的代码中,却发现存在文件冲突?
您可能见过一些 git 命令,如rebase
、reset
和revert
。如果这些命令吓到了你,你并不孤单。其中一些功能强大,如果您不知道如何使用它们,可能会破坏您的项目。但是不要担心,你将会学习如何使用它们。😊
向后移动
到目前为止,您习惯使用的命令git add
和git commit
已经将您的代码从左向右移动了。我们现在探索允许您在 git 区域中向后移动的命令,以便撤销您对文件所做的更改,或者完全恢复到以前的提交。
假设您正在处理一个文件,并意识到您所有的新更改都不会成功。您意识到您可以返回到上次提交时的文件版本,而不是手动计算如何撤销所有的更改。所以你执行git reset --hard HEAD [file name]
。
让我们解开这个命令。HEAD
指您之前的提交。这很有用,因为否则您将不得不寻找提交散列(尽管您可以使用git log
很容易地找到它)。
把reset
想象成那些起重机爪机器中的受控爪,你可能很高兴在小时候使用,或者更确切地说是被使用,以获得令人垂涎的填充动物玩具。
图片来自易贝
reset
允许您从存储库中的特定提交中挑选您想要的文件。然后,通过使用以下标志命令之一,您可以选择将这些文件放入哪个 git 区域:--hard
、--mixed
和--soft
。
正如你在上面看到的,git reset --hard HEAD [file name]
将从之前的提交中获取file name
,并将其应用于索引和工作区。
作者图片
另一方面,git reset --mixed HEAD [file name]
将从之前的提交中获取file name
,并将其应用于索引。**
作者图片
最后,git reset --soft HEAD [file name]
将从之前的提交中获取file name
,并将其应用于存储库。**
作者图片
恢复到以前的提交
假设您将一些东西推送到您的存储库中,但是它没有工作。只需执行一个git revert [commit hash]
,就可以轻松地返回到代码正在运行的前一次提交。记住,您可以通过执行git log
来找到您的提交散列。
就这么简单,但是有一点要注意。还原不会擦除您选择提交之前的任何历史记录。相反,它只是使用您选择恢复到的上一个提交的确切状态创建一个新的提交。这很好,因为它不会弄乱您的任何 git 历史。
编辑您的提交历史
在 git 的旅程中,您不需要花很长时间就能遇到一些您希望能够撤销的事情。例如,您向本地存储库提交了一个文件,但是您弄乱了提交消息。或者说,您进行了许多非常小的提交,您宁愿将它们组合成一个提交。这就是git rebase
大放异彩的地方。
假设您有一组小提交。
作者图片
您希望将它们合并在一起,然后给合并的提交一个新消息。要做到这一点,只需做git rebase --interactive origin/main
。
作者图片
那就去做我们想要的改变吧。在这种情况下,我们希望将最后两次提交压缩到第一次提交中。
作者图片
完成此操作后,继续保存文件并关闭,这将带您到一个新窗口来编辑最终的提交消息。
作者图片
我们对最终的提交消息进行了更改。
作者图片
现在您可以看到,我们之前的三次提交现在已经被合并或压缩为一次。
作者图片
重命名远程分支
假设有一个您想要重命名的分支,它已经被推送到您在 GitHub 或其他地方的远程存储库中。假设只有您一个人在处理这个分支(因为更改远程历史可能会有一些不好的影响),您可以通过执行以下操作来更改分支名称:
git checkout [your branch]
git branch -m [new branch name]
git push origin :[your branch] [new branch name]
git push origin -u [new branch name]
典型的 git 工作流
假设你和你的团队是 GitHub 中一个资源库的贡献者。您已经将一个main
分支作为您的默认主分支。但是你想知道如何相互协作,这样你就不会覆盖彼此的工作。以下是我过去经常使用的一个工作流程:
- 首先确保
main
分支与git checkout main; git pull
保持同步。 - 用
git checkout -b [your branch name]
从main
分支创建一个新分支。 - 添加那个新特性或者修复那个 bug,然后做一个
git push
把它推送到你的分支。 - 当您准备好将您的更改合并到
main
分支中时,在 GitHub 中创建一个 pull 请求,以便您的团队成员可以查看您的代码并提供任何改进建议。如果看起来不错,GitHub 会让你把修改合并到main
里。 - 现在只需清理您的本地存储库:1)用刚刚合并到远程
main
分支中的变更更新您的本地main
分支,用git checkout main; git merge your-branch
2)删除您工作的本地分支,git branch -d [your branch name]
。
现在,只需重复该工作流程,进行您想要的任何更改。👍
结束语
如果你想学习更多关于 git 的知识或者喜欢一个交互式的环境,请随意查看学习 Git 分支。
如果你想知道如何处理其他一些棘手的 git 场景,请查看 Dangit,git!?!。
到目前为止你已经学到了很多。您已经了解了 git 的四个区域,在这些区域中移动您的更改的命令,如何恢复到以前的提交,编辑您的提交历史,重命名分支,并且您已经看到了典型的 git 工作流的样子。
我希望这篇文章对您有所帮助,并且您已经对这些 git 命令的作用有了更好的理解!
*[## 想在数据科学方面变得更好吗?
当我在我发布独家帖子的媒体和个人网站上发布新内容时,请单击此处获得通知。](https://bobbywlindsey.ck.page/5dca5d4310)*
原载于 2020 年 10 月 7 日【https://www.bobbywlindsey.com】*。*
可解释机器学习指南—第 1 部分
我们为什么要相信模型?
机器学习模型目前正以巨大的速度变得流行。它们现在被用来解决各种领域的各种问题。在这个越来越受欢迎并因此在我们日常生活中变得越来越重要的时刻,有一个潜在的危险,现在也是非常重要的:机器学习模型的可解释性。
最近,我们看到了图像处理如何用于安全部门和卫生部门,回归和分类模型如何用于股票市场和投资部门。如果我们仔细观察,我们会依赖机器学习来做出肯定会影响我们的重要决定。所以,问题来了,我们为什么要相信模型?
换句话说,我们为什么要依赖一个智能算法做出的决定,甚至不知道它是如何操作的?你可以说有各种各样的算法,比如决策树和线性回归,它们非常容易理解和解释。但是,在当前世界中,研究人员和数据科学家正在不断尝试解决具有挑战性的问题,这些问题无法通过可解释的模型有效解决,因此需要非常复杂的算法或基于定制神经网络的算法。如果我们环顾四周,我们会发现大多数情况下,使用复杂的黑盒模型,我们很难回答这个问题**“算法是如何做到的?”**
事实证明这是一个合理的问题。通常,在设计模型时,数据科学家会关注一些问题或关于某些特征的一些规定,他们希望模型能够学习并用于预测。比如我可能喂人口预测“是否在该地区开店”。原因是我可能认为,如果人口多,我的销售额就会高,所以这两者之间的相关性非常高,所以我的模型将学习这一特征,该特征的重要性将会很高。我训练我的模型,它给了我很高的准确性,但它可能没有按照计划的方式学习。所以,我们需要在几种情况下确定这些事情,以增加准确性,并确定我们的预测。
我举一个经典的例子,在一个“西伯利亚哈士奇”和“狼”的图像分类中,设计了一个模型。它给出了非常高的精确度。模型分析的时候发现,模型从来没有把哈士奇和狼分类。它划分了“下雪”或“不下雪”。雪被预言为哈士奇,无雪为狼。大多数哈士奇图像有雪,准确性非常好。
所以,从上面的例子我们可以理解,实际解释我们的模型有多重要。
问题的解决方法
处理机器学习模型的解释的许多方法已经发展了多年。不同的方法使用不同的逻辑和理论来解释难以理解的“黑盒”算法,如神经网络、Xgboost 和随机森林。两种最重要和最受欢迎的方法是石灰和 SHAP。它们被用来解释局部的或观察到的预测。换句话说,它们给了我们一个清晰的直觉,让我们知道给定的特征如何影响我们模型中的预测,以及该特征对我们的模型有多重要。
在这篇文章中,我们将详细而简要地谈论石灰和 SHAP。我们将尝试使用著名的泰坦尼克号数据集来解释。
泰坦尼克号的沉没是历史上最臭名昭著的海难之一。
1912 年 4 月 15 日,在她的处女航中,被广泛认为是“不沉”的皇家邮轮泰坦尼克号在与冰山相撞后沉没。不幸的是,没有足够的救生艇容纳船上的每个人,导致 2224 名乘客和船员中的 1502 人死亡。虽然幸存有一些运气成分,但似乎某些群体比其他群体更有可能幸存。
在这个挑战中,我们要求你建立一个预测模型来回答这个问题:“什么样的人更有可能生存?”使用乘客数据(即姓名、年龄、性别、社会经济阶层等)。
现在,让我们先谈一谈数据集,然后再谈我们的目标。
数据预处理和准备
数据集看起来像这样,它有 12 列。“幸存”是我们的目标栏目,其他是专题栏目。现在,特性列需要一些预处理。
数据集提取片段:
import pandas as pd
df1=pd.read_csv('train.csv')
df2=pd.read_csv('test.csv')
df=pd.concat([df1,df2],axis=0)
我们将删除 PassengerID,因为它没有任何相关性。下一步,我们将采取“姓名”列,并根据他们的头衔将他们分配,类,像“夫人”,将类 1,“先生”类 2,等等。我们可以使用下面的代码片段做到这一点。
i=0
name=[]
while i<len(df):
#print(df.iloc[i]['Name'].type)
if "Mrs." in df.iloc[i]['Name']:
name.append(str(1))
print("a")
elif 'Mr.' in df.iloc[i]['Name']:
name.append(str(2))
print("b")
elif "Miss." in df.iloc[i]['Name']:
name.append(str(3))
print("c")
elif "Master." in df.iloc[i]["Name"]:
name.append(str(4))
print("d")
else:
name.append(str(5))
i+=1
df=df.drop(['Name'],axis=1)
df['name']=name
现在,我们转到 SibSp,这里我们也将创建一个具有两个类的特征,具有用 1 表示的兄弟,没有用 0 表示的兄弟,并将其包含在特征集中,删除原始特征。我们需要注意的一件事是,这些特征中的大多数都有 NaN,所以我们需要首先填充空值以避免错误。代码片段:
i=0
siblings=[]
df.fillna(0)
while i<len(df):
if df.iloc[i]['SibSp']>0:
siblings.append(str(1))
else:
siblings.append(str(0))
i+=1
df=df.drop(["SibSp"],axis=1)
df["Siblings"]=siblings
接下来,我们对 Parch 特性做同样的事情,我们设计了两个类,用 1 表示父类,用 0 表示父类,并使用列作为特性。
i=0
parent=[]
df.fillna(0)
while i<len(df):
if df.iloc[i]['Parch']>0:
parent.append(str(1))
else:
parent.append(str(0))
i+=1
df=df.drop(["Parch"],axis=1)
df["Parent"]=parent
我们选择“年龄”特征,也有一些 NaNs,我们将使用年龄列的平均值来填充它们。
df['Age'].fillna(df['Age'].mean())
然后,我们将创建一个具有 5 个类别的新特征列来分类和移除年龄的可变性质。我使用了给定的阶级界限:
年龄< 15 岁:1 级
15≤年龄< 30:2 级
30≤年龄< 45:3 级
45≤年龄< 60:4 级
年龄≥60 岁:5 级
15 岁以下的“年龄”归为 1 等。
代码:
i=0
age=[]
while i<len(df):
if df.iloc[i]['Age']<15:
age.append(str(1))
elif df.iloc[i]['Age']<30:
age.append(str(2))
elif df.iloc[i]['Age']<45:
age.append(str(3))
elif df.iloc[i]['Age']<60:
age.append(str(4))
else:
age.append(str(5))
i+=1
df['age']=age
df=df.drop(['Age'],axis=1)
对于“Fare”特性,我们将执行与“age”特性相同的操作。我们将用平均值填充 NaN,并将其分类到一些固定的桶中。以下是我对桶边界的估计。
票价<15: Class 1
15≤Fare<55: Class 2
55≤Fare<120: Class 3
120≤Fare<190: Class 4
Fare≥190: Class 5
Next, if we check the “Tickets” column, we will see a few special tickets with letters, and others are numeric. I have labeled the completely numeric ones 1 else 0. Snippet:
i=0
ticket=[]
while i<len(df):
if df.iloc[i]['Ticket'].isnumeric():
ticket.append(str(1))
else:
ticket.append(str(0))
i+=1
df['ticket']=ticket
df=df.drop(['Ticket'],axis=1)
Next, we do something similar to the cabin feature. Most of the entries in the “Cabin” feature are empty. We label the empty ones as 0 and others as 1.
z=0
df['Cabin'].fillna("NA")
cabin=[]
while z<len(df):
print(df.iloc[z]['Cabin'])
if 'NA' in str(df.iloc[z]['Cabin']):
cabin.append(str(0))
else:
cabin.append(str(1))
z+=1
Now, our preprocessing is complete. After the preprocessing our data looks like this.
We will now drop the Survived column and apply encoding on the categorical features. After application, we will obtain a Feature set X, of 31 columns i.e 31 features and a target set Y, of 1 column, Survived.
Index(['Survived', 'Pclass_1', 'Pclass_2', 'Pclass_3', 'Sex_female',
'Sex_male', 'Embarked_C', 'Embarked_Q', 'Embarked_S', 'name_1',
'name_2', 'name_3', 'name_4', 'name_5', 'Siblings_0', 'Siblings_1',
'Parent_0', 'Parent_1', 'age_1', 'age_2', 'age_3', 'age_4', 'age_5',
'fare_1', 'fare_2', 'fare_3', 'fare_4', 'fare_5', 'ticket_0',
'ticket_1', 'cabin_0', 'cabin_1'],
dtype='object')
These are our 31 features.
We split our dataset in X_train, Y_train, X_test, and Y_test.
from sklearn.model_selection import train_test_splitX_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.3, random_state=42)
Thus we have processed our dataset and are ready for operations.
We go back to our application.
LIME
LIME stands for Linear Model Agnostic Explanation. It is used for **当地口译。**它用于分析和解释底层黑盒模型的决策及其基于特定观察的预测,比如训练或测试数据集的特定行。现在,问题来了,它是怎么做到的?正如我们之前注意到的,有几个模型非常容易解释。LIME 使用这些可解释的模型来预测黑盒模型。
LIME 使用决策树和逻辑回归等模型来克隆黑盒模型给出的预测。它接收单个观察值,并通过对特征值进行一定程度的更改来创建一组观察值。因此,在尝试了所有的组合之后,LIME 获得了一个全新的特性集。然后将该特征集传递给黑盒模型,并获得相应的预测。现在,这些预测成为可解释模型的 Y 训练集或目标集,而特征集成为 X 训练集。
现在,LIME 有几个模型,如决策树、线性回归、套索回归和逻辑回归。目标可解释模型是所有可解释模型中产生最小损失的模型。这是因为最小损失函数意味着具有最大准确性的模型是克隆黑盒模型最好的模型。现在,由于可解释模型的行为类似于黑盒模型,解释它将给出黑盒模型的结果。
这是石灰的解释公式。这里,观察值 x 的解释由模型 f 或 g 中的一个示出,这取决于哪个将产生最小损失函数,并且 sigma(g)描述了保持尽可能低的解释模型的复杂性。现在,由 LIME 产生的数据的变化也给了解释模型获得特征边界的自由,例如,如果特征的值小于值 x,则它是 A 类,否则它是 b 类。
也有一些缺点。LIME 的问题在于它是一个局部算法,所以它的解释对整个数据集来说并不成立。
解释算法是如何工作的
一些最好的解释算法是决策树和逻辑回归。这里我们就来说说这两个算法。
**决策树:**用于非线性数据集。现在,该树接收一个数据集,并根据不同要素的边界值继续分割数据集。它多次分割数据集,并创建几个数据子集。最后的子集是叶节点。他们是分类班。内部节点是拆分节点。训练有助于树判断每个特征的边界值及其对决策路径的影响。
照片由克里斯托弗拍摄
因此,每个节点代表一个要素和一个与该要素相对应的值,该值充当边界。现在,每个决策树都想最小化**基尼系数。**基尼系数是实际值与预测值的差值。因此,在每个节点之后,如果我们试图判断与父节点相比系数减少了多少,我们将清楚地了解节点的权重。节点的权重描述了特征的重要性。
**Logistic 回归:**是基于线性回归算法的分类。这不适用于非线性模型。在线性回归中,我们用一个方程表示一个 n 维超平面来预测值。
Y=b0+b1x1+b2x2+………+bnxn
这里 b0 是截距,特征‘I’的 b{i}是回归中特征的系数或权重。它使用线性回归生成插值。
在分类中,我们预测概率,因此我们使用逻辑函数将值压缩在 0 和 1 之间。
就像这样,
照片由克里斯托弗拍摄
因此,我们获得概率为
现在,我们处理事件发生的概率和事件不发生的概率之比。P(y=1)/P(y=0)。这个比率叫做赔率。
这是我们的方程式。这里 p(y=1)/p(y=0) =n 意味着事件发生的几率是事件不发生几率的 n 倍。
现在,如果我们将特性 j (xj)的值增加 1,则等式变为:
这导致了,
因此,如果我们将一个特征增加 1 个单位,它对概率的影响将是其权重的指数。所以,它的贡献是它的重量的函数。因此,我们可以很容易地使用逻辑回归中的权重来获得特征贡献。
结论
在的下一部分,我们将看到可解释模型的应用和实际实现。我们还将看到 SHAP 应用程序。
可解释机器学习指南—第二部分
石灰的应用
在这一部分,我们将介绍石灰的应用。我们已经在本文的第 1 部分看到了 Titanic 数据集的数据预处理和 LIME 的理论和工作。我们有了预处理过的数据。接下来,我们转到石灰应用程序。
神经网络
首先,我们将使用 LIME 应用程序来设计基于神经网络的模型。
这是我们的神经网络课。我用过 TensorFlow Keras 的几层。我们的输入层接受 31 列的数据集。它有 4 个密集层,输出层有 1 个神经元来决定“存活”或“不存活”。输出函数为 sigmoid。我们将使用 Adam 优化器和二元交叉熵。
现在我们将调用并训练模型。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score,f1_scoremodel_nn=Neural_Network(X_train,Y_train, X_test,Y_test)
print(model_nn.predict_classes(X_train).astype(float))
在我们的训练集上,“predict_classes”函数生成一个二维列表。
如果我们观察,每个元素有 2 个值,第一个值是事件发生的概率,第二个值是不发生的概率。
现在,我们去吃酸橙。
import lime.lime_tabular
classes=['Survived','Not_Survived']df_k=df.drop(['Survived'],axis=1)
all_feat=df_k.columns
#model_gb.fit(X_train,Y_train)
print(model_nn.predict_classes(X_train).astype(float))
predict_fn_nn= lambda x: model_nn.predict_classes(x).astype(float)explainer = lime.lime_tabular.LimeTabularExplainer(X_train,mode='classification',feature_selection= 'auto',
class_names=classes,feature_names = all_feat,
kernel_width=None,discretize_continuous=True)
接下来,我们呼叫解释者。
observation_1=24
exp=explainer.explain_instance(X_test[observation_1], predict_fn_nn, num_features=5,top_labels=1)
exp.show_in_notebook()
这将产生如图所示的结果。现在,这里最左边的方框是两类的预测概率,“幸存”类和“未幸存”类。中间的图表显示了重要的特征及其边界值,而右边的表格是所通过的观察行中实际对应的特征值。
observation_1=42
exp=explainer.explain_instance(X_test[observation_1], predict_fn_nn, num_features=5,top_labels=1)
exp.show_in_notebook()
这是第二个可供参考的例子。
XGBoost
现在,让我们检查 XGBoost 的 LIME 解释器。下面的代码片段用于启动 XGBoost 的解释器。
from xgboost import XGBClassifier
classes=['Survived','Not_Survived']
all_feat=df_k.columns
model_xgb = XGBClassifier()
model_xgb.fit(X_train, Y_train)
predict_fn_xgb = lambda x: model_xgb.predict_proba(x).astype(float)
explainer = lime.lime_tabular.LimeTabularExplainer(X_train,mode='classification',feature_selection= 'auto',
class_names=classes,feature_names = all_feat,
kernel_width=None,discretize_continuous=True)
我们称之为解释者。
observation_1=32
exp=explainer.explain_instance(X_test[observation_1], predict_fn_xgb, num_features=5,top_labels=1)
exp.show_in_notebook()
现在小字体 0.54,0.15 等是特征的对应贡献,值 1.0,0.0 是边界值。
二审:
observation_1=86
exp=explainer.explain_instance(X_test[observation_1], predict_fn_xgb, num_features=5,top_labels=1)
exp.show_in_notebook()
我们也可以用其他形式获得这些结果。
exp.as_map()
结果:
{1: [(3, 0.5441379658340547),
(24, -0.15448119146031272),
(2, -0.1233271814431237),
(9, 0.11870380100373268),
(8, -0.11796994360496856)]}
这提供了这种形式的地图。1 是预测,列表有特征号和重要性。
exp.as_pyplot_figure()
随机森林
我们还将看到一个使用随机森林的实例。
from sklearn.ensemble import RandomForestClassifier
classes=['Survived','Not_Survived']
all_feat=df_k.columns
model_rf = RandomForestClassifier()
model_rf.fit(X_train, Y_train)
predict_fn_rf = lambda x: model_rf.predict_proba(x).astype(float)
explainer = lime.lime_tabular.LimeTabularExplainer(X_train,mode='classification',feature_selection= 'auto',
class_names=classes,feature_names = all_feat,
kernel_width=None,discretize_continuous=True)observation_1=47
exp=explainer.explain_instance(X_test[observation_1], predict_fn_rf, num_features=5,top_labels=1)
exp.show_in_notebook()
用可解释的模型解释
现在,在我们的理论中,我们已经看到一些可解释的模型被用来获得直觉。所以,让我们回溯一下,看看我们是否能得出类似的结果。
现在,我们不能通过调整要素来生成数据集,因为这是由 LIME 模块针对特定观测随机完成的。所以,让我们用整个数据集来试着解释结果。我们将使用我们的神经网络模型来这样做。
model_nn=Neural_Network(X_train,Y_train, X_test,Y_test)
pred=model_nn.predict_classes(X).astype(float)
print(pred)
结果:
[[4.87956345e-01 5.12043655e-01]
[1.00000000e+00 0.00000000e+00]
[9.99990702e-01 9.29832458e-06]
...
[2.51247525e-01 7.48752475e-01]
[3.47714871e-02 9.65228498e-01]
[5.00362515e-01 4.99637485e-01]]
这是我们对整个数据集上每个类的神经网络预测。现在,让我们从结果中得出预测
Pred=[]
for j in pred:
if (j[0]/j[1])>1:
Pred.append(1)
else:
Pred.append(0)
print(Pred)
这些是我们得到的预测。现在,这个列表将作为我们的目标集和整个数据集,没有保留的列作为我们的特征集。因此,实际上我们正在尝试创建一个行为类似于我们的人工神经网络模型的模型。
先用决策树模型来解读吧。
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(criterion = "gini",
random_state = 100,max_depth=6, min_samples_leaf=8)
clf.fit(X,Pred)
我们用 X 作为训练集,pred 作为预测集来拟合模型。
y_pred_tree = clf.predict(X_test)
accuracy_score(Y_test,y_pred_tree)
这给出了 87%的准确度,这对于我们的目的来说是非常好的。因此,我们的决策树模型表现为 87%的黑盒模型。现在,我们来试着解读一下。
from sklearn import tree
import graphviz
tree_data = tree.export_graphviz(clf, out_file=None,
feature_names=df_k.columns,
class_names=["Survived","Not_Survived"],
filled=True, rounded=True,
special_characters=True)
graph = graphviz.Source(tree_data)
#this will create an iris.pdf file with the rule path
graph.render("titanic")
这将给我们一个可视化的节点形成的树。
这是我们的树形成。每个节点都有一个关于节点的决策,该决策决定所获得的结果。从这里我们可以获得边界值。现在,我们来谈谈特性的重要性。
feat_importance = clf.tree_.compute_feature_importances(normalize=False)
print("feat importance = " + str(feat_importance))
print(len(feat_importance))
结果
feat importance = [0.00000000e+00 3.61136190e-03 1.39727482e-02 2.77128356e-01
0.00000000e+00 1.01837874e-02 3.36657108e-03 0.00000000e+00
3.66875884e-03 0.00000000e+00 4.96683522e-03 3.87192368e-03
0.00000000e+00 4.20667376e-04 4.60591655e-03 8.34158579e-03
0.00000000e+00 4.42596721e-03 6.48457847e-03 0.00000000e+00
8.54240790e-04 2.40717013e-04 0.00000000e+00 6.19429971e-04
6.36079009e-03 8.23858955e-05 0.00000000e+00 7.82349745e-03
3.09307607e-03 6.90860184e-03 0.00000000e+00]
31
这提供了 31 个特性重要性的列表,每个特性有一个值。
import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as pltobjects = df_k.columns
y_pos = np.arange(len(objects))
performance = feat_importancefig, ax = plt.subplots(figsize=(20, 20))
plt.barh(y_pos, performance, align='center', alpha=0.5)
fontsize=14,
plt.yticks(y_pos, objects,fontsize=20)
plt.xticks(fontsize=20)
plt.xlabel('Contributions')
plt.title('Feature Contributions',fontsize=20)plt.show()
上图显示了要素重要性图。因此,我们可以使用决策树从整体上对我们的神经网络模型得出一个非常清晰的直觉。
现在,让我们看看我们是否能从回归模型中得出一些东西。
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(solver='liblinear', random_state=0)
model.fit(X, Pred)
类似于这里的决策树,我们用 X 和 Pred 来复制我们的黑盒模型。
Weights=np.hstack((model.intercept_[:,None], model.coef_))
print(Weights)
print(len(Weights[0]))
结果:
[[ 0.12274084 0.5433339 0.21922863 -0.63982169 1.45315131 -1.33041046
0.0827389 0.22923408 -0.22390968 0.56095332 -1.14391015 -0.04797768
1.28082004 -0.52714469 0.30183458 -0.17909374 0.37075839 -0.24801754
0.37928751 0.22380996 0.12957386 -0.51560858 -0.09432191 -0.18315589
-0.00641827 0.49388734 -0.09299957 -0.08857277 0.10037632 0.02236453
-0.34099133 0.46373217]]32
因此,它返回每个特征的权重和 B0 偏差。这是一个包含 32 个元素的列表。所以,这些是我们的特征重要性。
上面的列表给了我们这个情节。所以我们看到,根据理论,我们可以从可解释的模型中得到直觉。
石灰也可以应用于图像。但是这里的做法有点不同。在那里,改变一个像素无关紧要。因此,形成了一组称为超像素的像素。这些超像素变成灰色,然后再次预测图像。通过这种方式,可以判断图像的哪些部分实际上会影响决策。
SHAP
SHAP 主张“沙普利附加解释”。它基于 Shapley 值,Shapley 值基于博弈联盟理论中的一种方法。它检查特定特征值如何影响模型预测。它首先采用两个特征,并尝试它们的组合,以检查它们如何影响预测,并为它们分配一些重要性。然后,它添加第三个特征,并检查它对预测的影响程度,再次为其分配权重,依此类推。SHAP 树解释器用于获取特性重要性。
import shapshap_values = shap.TreeExplainer(model_xgb).shap_values(X_train)
shap.summary_plot(shap_values, X_train, plot_type="bar")
这是应用 Xgb 产生的特性重要性图。
import shapshap_values = shap.TreeExplainer(model_rf).shap_values(X_train)
shap.summary_plot(shap_values, X_train, plot_type="bar")
这是通过应用随机森林生成的要素重要性图。
红色部分显示要素对 0 或“未存活”类的影响,蓝色部分显示 1 或“存活”类的影响。
结论
这是对人机学习解释库如何运行的基本洞察。希望这些文章有所帮助。
这是 Github 链接。
数据湖指南——现代批量数据仓库
照片由 弗拉德 Chețan 发自 像素
使用“功能数据工程”重新定义批量数据提取模式和数据湖
过去几十年,数据分析领域发生了巨大的变革。随着存储成本的降低和云计算的采用,指导数据工具设计的限制变得过时,因此—数据工程工具和技术必须发展。
我怀疑许多数据团队正在进行复杂的项目,以使他们的数据工程栈现代化,并使用他们所掌握的新技术。许多其他公司正在从零开始设计新的数据生态系统,这些公司正在寻求机器学习和云计算的进步所带来的新商机。
pre-Hadoop批处理数据基础架构通常由与其存储紧密耦合的数据仓库(DW)设备(例如 Oracle 或 Teradata DW)、提取转换加载(ETL)工具(例如 SSIS 或 Informatica)和商业智能(BI)工具(例如 Looker 或 MicroStrategy)组成。在这种情况下,数据组织的哲学和设计原则是由诸如 Ralph Kimball 的The Data Warehouse Toolkit(1996)或比尔·恩门的Building The Data Warehouse(1992)等书中概述的成熟方法所驱动的。
我将这种方法与其现代版本进行了对比,后者诞生于云技术创新和降低存储成本。在现代堆栈中,由数据仓库设备处理的角色现在由专门的组件处理,如文件格式(如 Parquet 、 Avro 、胡迪)、廉价云存储(如 AWS S3 、 GS )、元数据引擎(如 Hive metastore)、查询/计算引擎(如 Hive、拖放 ETL 工具不太常见,取而代之的是一个调度器/指挥器(例如 Airflow 、 Luigi )和“特设”软件逻辑来承担这个角色。“ad-hoc”ETL 软件有时出现在单独的应用程序中,有时出现在调度程序框架中,该框架通过设计是可扩展的**(气流中的操作符,Luigi 中的任务)。它通常依赖 Spark clusters 或 DWs 等外部计算系统进行大量转换。BI 方面也看到了称为超集的开源替代方案的兴起,有时由 Druid 补充,以创建汇总、在线分析处理(OLAP)立方体,并提供快速只读存储和查询引擎。**
存在的理由
我发现自己正在从事从前 Hadoop 堆栈到现代堆栈的迁移。这不仅是一次技术转变,也是一次重大的范式转变。在某些情况下,在建模和架构决策中,我们应该远离过时的智慧和最佳实践,理解我们为什么这样做是很重要的。我发现 Maxime Beauchemin 的资源非常有帮助,学习/理解 Apache Airflow 的设计选择在实现他提倡的方法(Airflow 是由 Maxime 创建的)时带来了很多实际的理解。本指南旨在采用一种固执己见的方法来定义和设计数据湖。
我挑选了一些特定的技术来使本指南更加实用,我希望其中的大部分技术也适用于现代堆栈中的其他工具。选择一种技术而不是另一种技术的动机通常是我的经验(或缺乏经验)的结果。例如,我会提到 AWS 工具,因为这是我有经验的云提供商。
这篇博文定义了 ETL 的 E,并描述了数据湖的角色。
提取,血统
在数据工程中,提取应该是在给定时间点实体状态的未改变快照。
具体来说,它通常涉及调用 API、抓取网站、从安全文件传输协议(SFTP)服务器获取文件、定期对运行数据库的副本运行查询以及从 S3 帐户复制文件。提取的结果存储在一个便宜的、可扩展的、高可用性的云存储中,比如 S3,可以永久保存*——或者只要合规允许就保存。这些提取产生的数据构成了数据湖。*
数据湖
不同的作家、博客作者和专业人士对“数据湖”和“数据仓库”有不同的定义。有时,他们的角色没有被清楚地陈述或者有重叠——以至于造成混乱,这两个词可以互换使用。数据湖的以下定义很简单,它清楚地将数据湖从数据仓库中分离出来,并将原始数据(数据湖的一部分)从派生的数据集(数据仓库/数据集市的一部分)中分离出来。
数据湖是一个存储库,包含所有由业务产生或收集的未处理的数据。因为此时没有业务逻辑应用于数据,保持不变,如果业务需求发生变化,任何分析(表格、数据科学模型)都可以从该来源重新创建。从不同来源提取数据是必要的,因为从来源获取数据通常是昂贵的(API 调用、缓慢的 SFTP、操作数据库转储),有时是不可能的(API 发展、sftp 变空、操作数据库就地改变记录)。
结构
随着数据的民主化和分析的去中心化,发现湖泊变得非常重要,考虑以下结构:
*s3://myorg-data-lake
├── s3://myorg-data-lake/tweets_mentioning_myorg
└── s3://myorg-data-lake/salesforce_clients*
数据湖消费者希望任何摘录都是“my org-Data-Lake”S3 存储桶中的顶级前缀,以便于浏览。然而,数据不需要放在同一个桶中,因为我们可以使用 Hive metastore 将任何提取注册为同一个模式中的表,提供一个到数据湖的中央接口。
*data_lake
├── data_lake.tweets_mentioning_myorg → s3://myorg-twitter-extracts/tweets_mentioning_myorg
└── data_lake.salesforce_clients → s3://myorg-salesforce-extracts/salesforce_clients*
格式
半结构化和非结构化数据经常被认为是数据湖的一个特征。然而,我相信在提取过程中,转换成一种嵌入了模式的文件格式(比如 Parquet 和 Avro)有很大的好处。模式是定义数据集接口的一种方式,使得多个团队在需要最少通信的情况下更容易使用它。这也是一种执行轻量级验证的方式,验证源系统仍然在生成预期的数据。当模式不再有效时,提取中断*,但是它降低了产生错误分析或转换过程中隐藏错误的风险。提取的数据可能已经有了某种模式(数据库和 API 提取),存储为 JSON / CSV 将意味着丢失一些有价值的元数据。*
在源系统使以前的数据很快不可用的情况下,提取一次而不进行任何转换,并在原始副本上运行转换。然后,配置单元表可以指向转换后的副本。例如,如果 we 文件来自 SFTP 服务器,并且这些文件在大约 1 小时后消失:
*SFTP
├── file1.csv
└── file2.csvs3://myorg-data-lake
├── s3://myorg-data-lake/sftp_clients/raw/file1.csv
├── s3://myorg-data-lake/sftp_clients/raw/file2.csv
└── s3://myorg-data-lake/sftp_clients/parquet/…*
通过这种方式,文件在 S3 上,模式可以被修复,而不用担心丢失任何数据。
由于大数据框架利用文件元数据和特殊数据布局来优化计算,因此生成的文件处理速度通常会更快。使用这种文件格式的另一个好处是减小了文件大小。模式、编码技术和特殊的数据布局*,如列存储,允许这些库避免冗余——减少表示相同信息所需的字节量。*
文件大小
许多 Hadoop 系列框架在处理少量大文件时比处理大量小文件时效率更高。原因是读取每个文件的元数据有开销,启动并行下载文件的进程也有开销。因此,合并从数据库的多个查询、多个 API 调用或多个 SFTP 文件中提取的数据以减少文件数量,可以为下游转换节省大量时间。像 Snappy 这样的压缩库也可以用来减少通过网络的数据量,并且通常值得这样做,因为引入的 CPU 负载可以忽略不计。
没有变化
数据湖应该包含在给定时刻**的状态*的不变真值 — 未修改快照。*此时任何转型都是不可取的— 合规流程除外(如匿名化)。
如果数据在被复制到数据湖之前被更改,它将偏离其在源系统中捕获的状态。如果源系统使以前的数据不可用,并且应用的逻辑需要撤销,数据将不得不进一步变异。这是危险的,因为它可能会导致无法回滚更改来获取原始提取。
有时,源系统中的错误会导致产生不正确的数据。在这些方面,我们可能希望它反映在我们的捕获(和分析)中,或者我们可能希望它得到纠正。在后一种情况下,重新运行相关时间窗口的提取过程可能就足够了,或者可能需要人工干预,但是在这两种情况下,物理分区仅用于覆盖相关数据。
物理分区
对提取的数据进行分区对于实现幂等性和优化提取大小非常重要。Maxime Beauchemin 在他的功能数据工程博客中为幂等 ETL 提供了一个强有力的案例。给定一个“预定的执行日期”,提取应该总是产生相同的结果。这有时是不可能的*,因为我们依赖于我们无法控制的外部资源,但是这些提取仍然应该被分组到分区中,以便下游的转换可以是等幂的。*
这是一个幂等每日摘录的简单示例:
*SELECT * FROM customers
WHERE last_modified_date >= 2020–01–01
AND last_modified_date < 2020–01–02*
在实践中,我们将参数化上面的 SQL 以从执行日期导出下限和上限
*SELECT * FROM customers
WHERE last_modified_date >= {{ execution date - 1 day }}
AND last_modified_date < {{ execution date }}*
将其与下面的错误示例进行比较,其中结果将根据外部因素(今天的日期)而变化:
*SELECT * FROM customers
WHERE last_modified_date > 2019–01–01*
在 Airflow 中,执行日期通常用于实现幂等。执行日期是一个强大的概念,它是一个不可变的日期在运行时赋予一个管道*(气流中的有向无环图)。如果 DAG 计划在2019–01–01 00:00:00运行,这就是执行日期。如果该管道失败,或者需要重新运行,则可以清除该特定运行的状态,然后它将获得相同的执行日期并产生相同的提取,条件是提取基于执行日期和幂等。这使得并行运行多个提取过程成为可能,并且通常用于回填数据。*
例如,我使用相同的(简单的)技术从 API 回填数据,并使用 Airflow 的特性来限制并行性,并自动重试或超时调用。这是气流的一个常见用例,通过幂等(非重叠)提取使其成为可能。
类似地,我们可以运行端到端 ETL 的多个实例。当试图重新处理一个不寻常的时间范围内的数据时,这尤其有用。例如,如果转换代码和定义的资源是针对每天的数据量进行测试的,那么很难知道相同的设置对于整个月的数据量会有什么样的表现。相反,ETL 的一个实例可以在被重新处理的一个月中的每一天启动——可能并行执行(这在 Airflow 中很容易实现)。
提取的物理分区应基于提取处理的计划运行日期。最简单的例子是每日批量处理的每日提取物:
*s3://myorg-data-lake/sftp_clients/parquet/ds=2020-01-01
s3://myorg-data-lake/sftp_clients/parquet/ds=2020-01-02
s3://myorg-data-lake/sftp_clients/parquet/ds=2020-01-03*
ds 代表日期戳,这里:执行日期
请注意表示 Hadoop 生态系统中的物理分区的key=value
格式——物理分区被转换为数据集的一列,当在 WHERE 子句中使用时,允许跳过所选分区之外的所有文件(分区修剪)。
提取过程应该以这样的方式编写,即根据给定的执行日期覆盖分区*。然后,转换过程可以使用ds
作为过滤器来获取它们需要处理的数据。***
其他时间框架遵循相同的原则,但在每小时的情况下,我们可能要考虑流*——批处理系统往往会有开销,使它们无法用于非常短的批处理。*
另一个有趣且常见的用例是,我们在提取数据的频率高于我们在转换数据的频率。当从源中提取一个大的时间范围使其超负荷时,就会出现这种需要,因为数据有以后不可用的风险,或者在转换之前一次提取所有数据会延迟下游过程。例如,我们可能希望创建每小时的提取,但是每 12 小时处理一次数据。为了设计这样一个需要以不同节奏调度任务的流水线,我们可以使用逻辑分支 到在我们需要的时候选择性地运行转换过程。
这种较高的提取频率会产生不希望的小文件,在这种情况下,在转换之前,我们合并要处理的批处理。当文件非常小,以至于将元数据(模式、统计数据)写入每一个文件会产生很大的开销时,应该在文件合并后转换为模式嵌入文件。在这种情况下,请考虑以下结构:
*s3://myorg-data-lake/sftp_clients/raw/ds=2020-01-01<space>00:00:00
s3://myorg-data-lake/sftp_clients/raw/ds=2020-01-01<space>01:00:00
s3://myorg-data-lake/sftp_clients/raw/ds=2020-01-01<space>02:00:00
...
s3://myorg-data-lake/sftp_clients/raw/ds=2020-01-01<space>12:00:00
--
s3://myorg-data-lake/sftp_clients/parquet/ds=2020-01-01<space>12:00:00*
其中合并+转换+压缩过程将把 12 个分区变成 1 个,并且sftp_clients
表将指向拼花版本而不是原始副本。
最后
在这篇文章中,我试图给数据湖和提供数据的提取过程添加一些结构。希望你觉得有用!
神经网络损失函数指南及其在 Keras 中的应用
二元交叉熵、余弦邻近度、铰链损耗,还有 6 个更多
损失函数是训练神经网络的重要部分,选择正确的损失函数有助于神经网络知道它有多远,因此它可以正确地利用其优化器。本文将讨论 Keras 支持的几个损失函数——它们是如何工作的,它们的应用,以及实现它们的代码。
交叉熵
二进制交叉熵在数学上定义为—
—给定正确的目标值 t 和预测值 p 。
给定正确目标值 0 的 p 值,二进制交叉熵值可以绘制为—
给定正确目标 1 的 p 值,二进制交叉熵值可以绘制为—
熵是对某一分布不确定性的度量,交叉熵是代表目标分布和预测分布之间不确定性的值。
#FOR COMPILING
model.compile(loss='binary_crossentropy', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.binary_crossentropy(y_true, y_pred, from_logits=**False**, label_smoothing=0)
分类交叉熵和稀疏分类交叉熵是二元交叉熵的版本,适用于几个类别。当一个样本有几个类别或标签是软概率时,应该使用分类交叉熵,当类别互斥时,应该使用稀疏分类交叉熵。
分类交叉熵:
#FOR COMPILING
model.compile(loss='categorical_crossentropy', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.categorical_crossentropy(y_true, y_pred, from_logits=**False**, label_smoothing=0)
稀疏分类交叉熵:
#FOR COMPILING
model.compile(loss='sparse_categorical_crossentropy', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.sparse_categorical_crossentropy(y_true, y_pred, from_logits=**False**, axis=-1)
余弦近似/余弦相似
余弦相似性是两个向量之间相似性的度量。数学表示是—
—给定两个向量 A 和 B ,其中 A 表示预测向量, B 表示目标向量。
较高的余弦接近度/相似度表示较高的准确度。完全相反的向量的余弦相似度为-1,完全正交的向量的余弦相似度为 0,相同的向量的余弦相似度为 1。
余弦近似可以在 Keras 中实现:
#FOR COMPILING
model.compile(loss='cosine_proximity', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.cosine_proximity(y_true, y_pred, axis=-1)
铰链损耗
铰链损耗在数学上定义为—
—给定预测 y 和目标值 t 的1。注意 y 应该是一个概率而不是一个单一的类标签。
下面是铰链损耗图,它是线性负值,直到达到 1 的 x 。
#FOR COMPILING
model.compile(loss='hinge', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.hinge(y_true, y_pred)
Hinge 还有另外一个离经叛道的,平方 hinge ,这(正如你能猜到的)就是铰链函数,平方。
#FOR COMPILING
model.compile(loss='squared_hinge', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.squared_hinge(y_true, y_pred)
胡伯损失
Huber 损耗在数学上定义为
…对于系数 c ,其中 t 表示目标值和预测值之间的差值,可绘制为
…对于 c. 的各种值
这可以在 Keras 中实现为
#FOR COMPILING
model.compile(loss='huber_loss', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.huber_loss(y_true, y_pred, delta=1.0)
双曲余弦的对数
双曲余弦函数是 log(cosh( x )),图形如下
…其中 x 表示预测值和目标值之间的差值。
#FOR COMPILING
model.compile(loss='logcosh', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.logcosh(y_true, y_pred)
对数误差平方
平均绝对误差的对数或 log( x )如下图所示。
误差平方的对数比较特殊,因为如果误差更接近 0(比如 0.2 到 0.1),那么误差减少 0.1,损失函数的下降幅度会比更大(比如 1.2 到 1.1)。
这可以在 Keras 中实现为:
#FOR COMPILING
model.compile(loss='mean_squared_logarithmic_error', optimizer='sgd')
# optimizer can be substituted for another one#FOR EVALUATING
keras.losses.mean_squared_logarithmic_error(y_true, y_pred)
如果你喜欢,看看其他帖子:
非最小相位系统指南
向导
为什么淋浴时打开热水旋钮时,水是先冷的?
你有没有想过,为什么当你在淋浴时打开热水旋钮,水会冷几秒钟,然后变热或反之亦然?
在本文中,我们将通过触及两个概念来回答这个问题,即最小相位 (MP)系统和传递函数 s
介绍
让我先阐述一下这个问题。我在问题中提到的现象在气候条件下尤其明显,那里的水供应与环境温度不同。我在两种极端气候下都生活过。在一个炎热的国家,夏天温度达到 45 摄氏度(113 华氏度),如果你转动冷水旋钮,供水首先是超热的,然后过一段时间,它会变冷。另一方面,我目前住在一个冬天温度达到零下 20 摄氏度甚至更低的城市。如果你转动热水旋钮,供水需要几秒钟变热。从控制工程的角度来看,这两种现象遵循相似的行为。
作者图片
本文将从控制系统理论的角度回答这个问题。简而言之,因为淋浴的供水系统是一个非最小相位(NMP)系统。
为了更好地理解 NMP 系统的行为,让我们来看一个例子。我们的系统需要一个数学模型。由于使用传递函数模型可以更好地理解 NMP,我们需要了解这个概念。然而,由于这种表示方式不如微分方程等其他数学模型常见,因此我们先简单介绍一下传递函数是什么,以及如何用这种方式表示模型。如果你已经知道什么是传递函数,那么可以直接跳到例子中。
物理系统的数学模型有几种表示方式,如微分方程、状态空间表示、传递函数。最常见的是,我们使用基于时间的函数,通过前面提到的两种方法来为物理系统建模(为了清楚起见,我们可以在频域中编写模型的微分方程,但这在物理系统的数学建模中不太常见)。然而,传递函数为我们提供了另一种看待系统的方式,即在频域中分析系统。现在,让我们看看什么是传递函数!
传递函数是什么?
传递函数模型使用多项式的比率来描述系统的输入-输出关系。因此,一个输入信号给一个系统产生一个受控输出(又名响应)。这种类型的建模不同于使用微分方程和状态空间表示,其中模型动态是可用的。
💡传递函数是观察动态系统的另一种方式,但在频域中,通过分析给定输入信号的系统响应。
控制系统的传递函数是输出信号的拉普拉斯变换 ( L {})与输入信号的拉普拉斯变换之比。简而言之,这里的目标不是使用基于时间的微分方程在时域中分析模型,而是使用变换在频域中分析模型。
输入和输出信号位于 s 域的系统框图(图片由作者提供)
假设我们有一个系统,以 u(t)和 y(t)作为输入和输出信号。传递函数可以如上所示进行计算。
分子多项式的根称为模型零点,分母多项式的根称为模型极点。零点影响系统的输入,极点影响系统响应及其稳定性。对于零极点分析,我们应该使用s-平面,它是一个复平面,拉普拉斯变换在其上绘制[1]。
注:本文中,我们只对系统的初始响应感兴趣,是为了回答为什么淋浴先冷后热?如前所述,由于初始系统响应与系统零点密切相关,因此我们将不讨论极点(这可能是另一篇文章的主题)。
什么是非最小相位系统?
现在我们已经熟悉了 NMP 系统,让我们正式定义这个系统:
在我们的系统中有一个延迟或者在s-平面(又名右半平面或 RHP)的右半部分有一个零模型可能导致一个非最小相位系统。
请注意,对于给定的幅度响应,只有一个最小相位系统,但 NMP 系统的数量是无限的。这就是为什么我们听不到像最大相位系统这样的术语。关于非最小相位系统的数学描述的更多细节可在[3]中获得。
个案研究
现在我们已经熟悉了传递函数,让我们看看非最小相位系统是什么样子,并回答为什么水在变热之前会先变冷!
下面是两个极点相同但零点不同的系统。系统 1 在 s = -2 处有一个零点,而系统 2 在 s = 2 处有一个零点。
MP 和 NMP 系统示例框图(图片由作者提供)
让我们分离系统 1 的极点和零点进行分析。如前所述,您可以将零视为修改后的输入(姑且称之为 U’(s))。如前所述,我们对本文中的模型零点感兴趣,因此我们将重点关注绿色模块。
由极点和零点分隔的最小相位系统的框图(图片由作者提供)
让我们通过应用逆 L 变换来看看系统 1 的修改输入 U’(s)在时域中的情况
按照系统 2 的相同过程,系统 2 的修改后的输入将为
所以,唯一的区别是负号。让我们画出两个系统的输入和修改后的输入信号,看看两者有何不同。
让我们将用作输入信号 u(t)(顶部的灰色函数)。由于输入信号是单位阶跃,输出 y(t)被称为阶跃响应。修改后的输入 u’(t)如下图所示,它是 2u(t)和 u(t)的导数之和。u’(t)的导数分量对于系统 1 是蓝色的,对于系统 2 是红色的。
MP 和 NMP 系统的带导数方向的输入和修改输入信号(图片由作者提供)
系统 2 中 u(t)的负导数导致系统 2 的阶跃响应首先向预期响应(稳态值)的相反方向移动,然后向预期响应(红色曲线)移动。这与系统 1 的阶跃响应(蓝色曲线)形成对比,系统 1 在开始时没有这种下冲。
MP 和 NMP 系统的阶跃响应,NMP 阶跃响应在开始时有一个下冲(图片由作者提供)
参考文献中有一个很好的例子。[4].
那么,下一个问题是,当我们有一个非最小相位系统时,该怎么办?
解决办法就是等待⌛.我们必须等到下冲结束。我们也可以为这样的系统设计一个控制器/补偿器。然而,由于几个原因,如系统变得不稳定或响应较慢的风险,设计 NMP 系统的控制器更加困难。
现在,让我们回到开头的问题。为什么在还没热之前就打开热水供应,淋浴的水却先凉了?
答案是,当你打开淋浴的热水供应时,系统会经历一个欠冲,因为这是水变热之前的一个非最小阶段。在这种情况下,最好等待几秒钟,以便系统(从欠冲中)恢复。你不应该改变方向或打开另一个旋钮,因为从长远来看,这将导致更冷的淋浴!
另一个通常在控制系统书籍中使用的例子是响应升降舵偏转的飞机高度变化。在这种情况下,当飞机试图使用升降舵增加高度时,由于飞机在增加高度之前向下倾斜(导致向下的空气动力),高度会稍微降低。这个例子可以从富兰克林的《动态系统的反馈控制》(第 7 版)[5]一书第 6 章的数学模型中得到。
结论
在本文中,我们了解了什么是非最小相位系统,以及为什么这样的系统首先经历错误方向的响应(你转动热水旋钮,水首先是冷的!).我们还讨论了传递函数,以及它在系统分析中的作用。
Jupyter 笔记本包含用于创建案例研究的阶跃响应的代码,可点击此处获取。感谢阅读!
最初发表于T5【https://www.ealizadeh.com】。
参考
[1] MATLAB,什么是传递函数模型? (2020)
[2]维基百科。2020.最小相位,(2020)
[3]Jesse b . Hoagg & Dennis s . Bernstein,《非最小相位零点——无事可做(经典控制再探——第二部分)】【T3》,(2007 年 6 月),IEEE 控制系统杂志。
[4] MATLAB,实践中的控制系统,第 6 部分:什么是非最小相位系统? (2019)
[5] G. F .富兰克林,J. D .鲍威尔,a .埃马米-奈尼,动态系统的反馈控制,第七版
从推特上收集回复的指南
使用 Octoparse 抓取 tweet 回复的初学者指南
我使用 Octoparse 进行抓取。我必须说,这个软件有这么容易使用的界面,他们的团队真的做了很大的工作,为您提供教程和文章,开始使用这个软件。此外,您可以将您的疑问放在他们官方网站的帮助页面上,他们团队中的某个人一定会给您一个有效的答复。
注意:这里,我用的是 OCTOPARSE 版本,先从它开始安装并注册。
如果你想购买该软件,请在这里浏览所有计划:http://agent.octoparse.com/ws/436
对于本教程,按照以下步骤收集唐纳德·特朗普的推文回复 :
**第一步:**登录后,你会看到这个软件的一个主页→转到最左上角的“+New”按钮→点击它→从下拉菜单中选择一个高级选项→新任务窗口将打开→新建一个组来组织你在那个(可选)→输入你要抓取的网页网址(如https://Twitter . com/realDonaldTrump/status/128711918732487444
**第二步:**保存后,你会看到一个弹出窗口,要求在两个选项中进行选择:要么自动让 Octoparse bot 抓取网页,要么手动选择特定元素。
- 因此,在这里我选择了第一个选项→单击它将显示如下内容:
- 当它完成 100%检测后,它会显示一个如下的窗口,有 3 个窗口界面同时打开。“提示”弹出框包含一些设置,您可以在保存前更改,也可以在自动检测结果之间切换,数据预览是一个包含数据集的界面,您可以预览抓取后的效果,Twitter 网页将在后台界面中打开。
- 在保存设置时,你会看到一个如下的窗口,有 4 个分割的窗口→现在,你可以很容易地看到工作流程和管理每个动作的设置。
- 这是机器人自动完成的所有工作,现在我们的任务是清理数据并相应地更改设置。在我们继续清理和更改设置之前,让我们创建一个分页循环。
由于 Twitter 没有下一步按钮或者被分成页面,机器人不会自动分页。否则,它会识别出来的。因此,要创建分页循环,我们必须设置一个无限滚动来通过分页加载更多数据。
- 为此,点击网页上的空白区域,如下图所示**【十字】** →然后,弹出窗口将打开→点击循环点击单个元素。
- 您将在工作流程中看到以下变化:
步骤-4: 现在,我们的下一步是相应地更改设置,让我们从分页开始→转到工作流中的“单击以分页”选项→悬停在它上面&您将看到“动作设置”选项→单击它→并在设置中指定这些更改,如下图所示:
- AJAX 超时对于在滚动后加载数据是必要的,否则,它不会抓取任何数据并返回一个错误报告。因此,到目前为止,5s 是机器人可以自动加载数据的一个很好的平均值。
- 指定滚动的细节是必要的,因为我们正在为无限滚动设置分页循环。因此,选中“加载后向下滚动页面”框,选择“一屏显示”。因此,它将在 2 秒的等待时间内抓取屏幕上列出的所有内容。
- 现在转到工作流中的“分页”选项→转到其动作设置→然后选择“退出循环”设置并指定“重复次数”以获取更多数据:
步骤 5: 现在我们的下一个任务是清理数据,我只为我的项目提取推文回复。还可以提取类似“转发”、“点赞”等细节。因此,如果其他属性对您的项目有意义,您也可以选择保留它们。
- 要删除任何特定属性→转到下面的数据预览部分→然后,选择任何列标题→您可以在角上看到 3 个点“…”→右键单击它→然后从下拉列表中选择“删除”选项。
- 还有其他的选择,你也应该试试。
- 删除所有不必要的属性后,清理后的数据集将如下所示:
步骤 6: 在做了一些更改之后,如果您正在获得预期的数据,那么您就可以运行您的任务了。
- 只需简单地保存任务并点击运行以提取更多数据。
- 将出现另一个弹出窗口,询问您希望如何运行您的任务。因此,免费用户只能选择“在你的设备上运行任务”。如果您希望提取超过 10000 行的数据,并希望在预定的基础上进行所有操作,您可以升级到高级计划,该计划并不昂贵,但具有所有这些有效的功能,如果您选择升级的计划,他们甚至会在预定的基础上为您进行清理,并返回一个干净的数据集。
- 因此,我在这里选择第一个选项,因为我没有收集那么多数据。
- 这是我提取的数据的样子:
- 如果您完成了提取或如果您开始得到重复→只需简单地“停止运行”,然后单击“导出数据”。
- 现在,只需导出:
- 如果您的数据中有重复项,它会自动检测到这些重复项,您只需选择删除这些重复项。
- 数据科学的人在这里完全可以理解我,因为获得一组独特的数据是一项真正的任务。
- 导出数据后,您将看到另一个窗口,询问您机器上数据集的另存为格式。
- 简单地,选择任何格式→按下“ OK ”,在下一个弹出窗口看到你保存的文件的目录,并简单地从那里打开它。
- 看看我刮下来的 Excel 文件:
- 因此,正如你所看到的,它是多么漂亮地刮掉了所有的文字,没有图像和视频。不过,如果你去看看它的网页,你会发现每个回复里都有视频和图片。
注 —该软件的使用因网站而异。如果你想抓取除此之外的任何网页,你必须改变一些设置。请参考他们的文章和教程。大约,他们的团队已经覆盖了所有类型的网页,无论是静态的还是动态的。
所以,如果我的博客帖子对你有所帮助,而你此刻觉得很慷慨,请不要犹豫,请给我买杯咖啡。☕😍
是的,点击我。
And yes, buying me a coffee **(and lots of it if you are feeling extra generous)** goes a long way in ensuring that I keep producing content every day in the years to come.
您可以通过以下方式联系我:
- 订阅我的 YouTube 频道 视频内容即将上线 这里
- 跟我上 中
- 通过 LinkedIn 联系我
- 跟随我的博客之旅:-https://kajalyadav.com/
- 成为会员:-【https://techykajal.medium.com/membership】
也可以看看我的其他博客:
有趣的项目想法与源代码和参考文章,也附上一些研究论文。
towardsdatascience.com](/8-ml-ai-projects-to-make-your-portfolio-stand-out-bfc5be94e063) [## 用 10 个简单的步骤搜集 1000 篇新闻文章
如果你遵循这 10 个简单的步骤,使用 python 进行网络抓取是非常简单的。
towardsdatascience.com](/scraping-1000s-of-news-articles-using-10-simple-steps-d57636a49755)**
用熊猫和 BeautifulSoup 抓取 HTML 表格的指南
也是一个实际的例子
抓取网页时碰到 HTML 表格是很常见的,如果没有正确的方法,从这些表格中提取有用的、一致的数据可能会有点棘手。
在本文中,您将看到如何使用两种不同的方法快速、高效地抓取这些元素:仅使用 Pandas 库和使用传统的抓取库 BeautifulSoup。
举个例子,我刮了英超分类表。这很好,因为这是一个常见的表格,基本上可以在任何体育网站上找到。虽然有必要通知您这一点,但当您阅读时,表被刮擦不会产生太大的影响,因为我试图使这篇文章尽可能地一般化。
pandas.read_html():快捷方式
如果你想要的只是从一个页面中获取一些表格,而不是别的,你甚至不需要设置一个完整的刮刀来完成这项工作,因为熊猫可以自己完成这项工作。pandas.read_html()
函数使用一些抓取库,如 BeautifulSoup 和 Urllib 来返回一个包含页面中所有表的列表作为数据帧。你只需要传递页面的 URL。
dfs = pd.read_html(url)
您现在需要做的就是从列表中选择您想要的数据帧:
df = dfs[4]
如果您不确定列表中帧的顺序,或者如果您不希望您的代码依赖于这种顺序(网站可能会改变),您可以随时搜索数据帧,通过其长度找到您正在寻找的数据帧…
for df in dfs:
if len(df) == 20:
the_one = df
break
…或者按其列的名称,例如。
for df in dfs:
if df.columns == ['#', 'Team', 'MP', 'W', 'D', 'L', 'Points']:
the_one = df
break
但是熊猫并没有让我们的生活变得更容易。这个函数接受一些有用的参数来帮助您获得正确的表。您可以使用match
来指定表应该匹配的正则表达式字符串;header
获取带有您传递的特定标题的表格;例如,attrs
参数允许您通过表的类或 id 来标识表。
然而,如果你不是只抓取表,而是使用,比方说,获取页面的请求,那么鼓励你将page.text
传递给函数,而不是 URL:
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
dfs = pd.read_html(page.text)
如果您使用 Selenium 的 web 驱动程序来获取页面,情况也是如此:
dfs = pd.read_html(driver.page_source)
这是因为这样做可以显著减少代码运行的时间,因为read_html()
函数不再需要获取页面。检查每个场景中一百次重复所用的平均时间:
Using the URL:
Average time elapsed: 0.2345 secondsUsing page.text:
Average time elapsed: 0.0774 seconds
使用 URL 使得代码速度慢了三倍。因此,只有当您不打算首先使用其他库获得页面时,使用它才有意义。
用 BeautifulSoup 获取表格的元素
虽然熊猫真的很棒,但它不能解决我们所有的问题。有时候,您需要按元素来抓取一个表,可能是因为您不想要整个表,或者是因为表的结构不一致,或者是出于其他任何原因。
为此,我们首先需要了解 HTML 表格的标准结构:
<table>
<tr>
<th>
<th>
<th>
<th>
<th>
<th>
<th>
</tr>
<tr>
<td>
<td>
<td>
<td>
<td>
<td>
<td>
</tr>
<tr>
<td>
<td>
<td>
<td>
<td>
<td>
<td>
</tr>
.
.
.
</table>
其中,tr
代表“表格行”,th
代表“表头”,td
代表“表格数据”,数据以文本形式存储在这里。
该模式通常是有帮助的,所以我们剩下要做的就是使用 BeautifulSoup 选择正确的元素。
首先要做的是找到桌子。find_all()
方法返回满足我们传递给它的需求的所有元素的列表。然后,我们必须在列表中选择我们需要的表:
table = soup.find_all('table')[4]
例如,根据网站的不同,有必要指定表类或 id。
剩下的过程现在几乎是直观的了,对吗?我们只需要选择所有的tr
标签和它们里面的th
和td
标签中的文本。我们可以再次使用find_all()
来查找所有的tr
标签,是的,但是我们也可以以更直接的方式迭代这些标签。
children
属性返回一个 iterable 对象,所有标签都在父标签的正下方,父标签是table
,因此它返回所有的tr
标签。因为它是一个可迭代的对象,我们需要像这样使用它。
之后,每个child
都是tr
标签。我们只需要提取其中每个td
标签的文本。以下是所有这些的代码:
for child in soup.find_all('table')[4].children:
for td in child:
print(td.text)
过程就完成了!然后你就有了你要找的数据,你可以用最适合你的方式操作它。
其他可能性
例如,假设您对表格的标题不感兴趣。不使用children
,您可以选择第一个tr
标签,它包含标题数据,并使用next_siblings
属性。这和children
属性一样,将返回一个 iterable,但是带有所有其他的tr
标签,它们是我们选择的第一个标签的兄弟标签。你将会跳过表格的标题。
for sibling in soup.find_all('table')[4].tr.next_siblings:
for td in sibling:
print(td.text)
就像孩子和下一个兄弟姐妹一样,你也可以寻找上一个兄弟姐妹、父母、后代等等。可能性是无穷无尽的,所以请务必查看 BeautifulSoup 文档,为您的铲运机找到最佳选择。
现实生活中的一个例子
到目前为止,我们已经编写了一些非常简单的代码来使用 Python 提取 HTML 表格。然而,当真正这样做时,你当然会有一些其他的问题要考虑。
例如,你需要知道如何存储你的数据。会直接写在文本文件里吗?还是将它存储在一个列表或字典中,然后创建*。csv* 文件?还是会创建一个空的数据帧并用数据填充它?当然有很多可能性。我的选择是将所有内容存储在一个大的列表列表中,该列表列表稍后将被转换为 DataFrame 并作为导出。csv 文件。
在另一个主题中,您可能希望在代码中使用一些try
和except
子句,让代码准备好处理一些可能会发现的异常。当然,为了不使服务器过载,您还需要插入一些随机的暂停,并且利用代理提供者,比如 Infatica ,来确保只要还有表需要清理,您的代码就会一直运行,并且您和您的连接会受到保护。
在这个例子中,我在整个 2019/20 赛季的每一轮比赛后都用我在本文中介绍的大部分内容刮出了英超积分榜。这是它的全部代码:
一切都准备好了:使用children
属性收集表中的所有元素,处理异常,将数据转换成 DataFrame,导出一个. csv 文件,并随机暂停代码几秒钟。在这一切之后,这段代码收集的所有数据生成了这个有趣的图表:
作者图片
你不会在互联网上找到绘制这样一个图表所需的数据。但是这就是抓取的好处:你可以自己去获取数据!
作为总结,我希望这是有用的,并且当你再次抓取 HTML 表时不会有问题。如果你有问题,有建议,或者只是想保持联系,请随时通过 Twitter 、 GitHub 或 Linkedin 联系。
感谢阅读!
文本注释指南——理解语言的关键
命名实体识别、情感分析等等
在一个越来越受数据驱动的数字世界中,企业必须利用用户在其平台上提供的大量数据,将自己与竞争对手区分开来。文本形式的数据是最常见、最丰富、最复杂的数据之一,从产品评论到社交媒体评论,理解组织或项目环境中的大量文本至关重要。
不幸的是,随着文本中大量信息的出现,处理这些非结构化的、机器不可读的数据也面临着挑战。与图像或录音等固有的数字数据不同,文本更加复杂。虽然通过自然语言处理(NLP)中的矢量编码(处理和建模文本的机器学习领域)等方法,模型可以表示单词的表层表示,但它必须手动学习(如果它学习的话)单个单词下面的深度。
标准的简单矢量化程序。每个单词代表向量中的一个索引,所以单词之间没有实际的联系或理解。
举个例子,单词“banks”有两个意思,比如“他刚刚抢劫了那些银行!”“河水又溢出了堤岸。”不管上下文如何,标准的矢量化模型都会认为这些是相同的。单词“banks”也是“bank”的复数形式,但天真的模型将无法绘制这些智能连接,而是标记,认为它们就像“apple”和“book”一样不同。没有人类的帮助,机器的简单文本到数组的转换缺乏人类操纵和创造语言的深度和独创性。出于这个原因,为非结构化文本提供高质量、信息丰富且准确的注释,以使机器对人类语言有更深入、更丰富的理解,这一点非常重要。这包括增加人类的解释,以提高机器的智能。创建各种标签的过程被称为文本注释,这些标签对人类来说是本能的,但却为人工智能提供了大量需要的信息。
例如,考虑命名实体标记,这是文本注释中的一个常见任务,其中非结构化文本中的实体(名词)被识别并被分配一个类别。这些可能包括组织、国家、位置、时间表达式、数量、货币、百分比、名称等。由于范围和定义的利益冲突,命名实体标记通常是极其复杂的。例如,考虑文本样本:“2006 年,亚历山大在弗吉尼亚的家中购买了 200 股美国银行股份。这相当于她投资组合的 30.5%。”理想情况下,完美的命名实体识别系统应该能够输出:“亚历山大[人名]于 2006 年[时间]在她位于弗吉尼亚[位置]的家中购买了 200[数量]股美国银行[公司]。这相当于她投资组合的 30.5%。命名实体标记有许多复杂之处——例如,“America”是一个国家,但是聪明的注释者会认识到这个实体本身嵌套在一个更大的实体中——美国银行,这是一个公司。
嵌套实体的一个常见示例。
或者,考虑这个句子,“亚历山大居住在弗吉尼亚州。”亚历山大港本身是弗吉尼亚州内的一座城市,但大多数人类注释者有一种本能的预感,亚历山大港。注释者能否区分“巴黎[城市]”、“希尔顿[公司]”、“巴黎希尔顿[人名]”?能够成功执行 NER 的智能模型——命名实体识别、自动命名实体标记——依赖于最初手动标注的文本样本。通过在我们的对话中提供对象背后的含义——通常是深层次的本能,并根据上下文而变化——模型的理解得到了加强。
情感分析是另一种常用的数据注释方法,在商业中有很强的直接应用。这个过程包括将文本样本中表达的观点按照积极、中立或消极的尺度进行分类。情感分析的一个细分还包括“主观性”——衡量一段文本有多固执己见(相对于更多事实内容而言)。
巧妙运用情感分析对做出商业决策至关重要。例如,假设你是一家应用公司的业务主管。您的应用程序在 app store 中的五星评级长期保持在 4.9/5,您的分析师已确定无需对应用程序进行进一步更改,因为最佳版本已经创建。然而,看似矛盾的是,你的 app 使用量在慢慢下降。如果进步的最明显和最公开的信号——评级——表明应用程序运行良好,这怎么可能呢?在对你的应用的评论(从应用商店到流行的技术博客)进行情绪分析后,你发现对你的应用的总体情绪实际上是负面的。喜欢这个应用程序的人更有可能对它进行评级,而不是全部删除,这产生了一个有偏见和淡化的进度信号。根据您的发现,您批准了进一步的开发,并看到您的应用程序使用量飙升。
总之…
- 为了给模型更多的视角和理解,文本模型需要文本注释。
- 命名实体识别是对文本中不同实体结构的标注。
- 情感分析是一种从文本中提取人类情感和印象的方法。
文本分类和情感分析指南
如何利用机器学习对语句的情绪进行分类?
**动机:**文本分类和情感分析是一个非常常见的机器学习问题,被用在很多活动中,比如产品预测、电影推荐和其他一些活动。目前,对于像我一样对这个领域不熟悉的每个机器学习者来说,探索这个领域已经变得非常重要。在探索这个话题后,我觉得,如果我通过一篇文章分享我的经验,它可能会帮助一些试图探索这个领域的人。所以,我会试着从一个基本的层次来构建整个东西,所以这篇文章可能看起来有点长,但是如果你想的话,它有一些部分你可以跳过。
像情感分析这样的文本分类问题可以使用多种算法以多种方式实现。这些主要分为两大类:
- 单词包模型:在这种情况下,我们数据集中的所有句子都被标记化,形成一个单词包来表示我们的词汇。现在,我们数据集中的每个单独的句子或样本都由单词包向量表示。这个向量称为特征向量。比如‘今天是晴天’,和‘太阳从东方升起’是两句话。单词包将是两个句子中唯一的所有单词。
- 第二种方法是基于时间序列的方法:这里每个单词由一个单独的向量表示。所以,一个句子被表示为一个向量的向量。
这是两种方法之间最基本的区别。现在,我们来详细看看。
为此,我们将使用 IMDB 审查数据集。它有 50,000 条评论,它们相应的情绪被标记为“正面”和“负面”。因此,首先,我们需要检查和清理我们的数据集。
初始数据集
from bs4 import BeautifulSoupdef strip_html(text):
soup = BeautifulSoup(text, "html.parser")
return soup.get_text()import redef remove_between_square_brackets(text):
return re.sub('\[[^]]*\]', '', text)def denoise_text(text):
text = strip_html(text)
text = remove_between_square_brackets(text)
return textdef remove_special_characters(text, remove_digits=True):
pattern=r'[^a-zA-z0-9\s]'
text=re.sub(pattern,'',text)
return text def Convert_to_bin(text, remove_digits=True):
if text=='positive':
text= 1
else:
text=0
return text
这些是清理评论部分所需的预处理,Convert_to_binary 用于将“正面”情感转换为 1,将“负面”情感转换为 0。因此,在预处理之后,我们得到:
预处理后
如果我们计算出我们的目标阶级划分。
我们有 25k 的正面情绪和 25k 的负面情绪。所以,让我们把数据分开。
X=df['review'].values
Y=df['sentiment'].values
X_train, X_test, Y_train, Y_test= train_test_split(X,Y, test_size=0.3)
第一种方法
矢量器:词汇袋
在这种方法中,我们使用词汇表中的所有单词来创建单个特征向量,词汇表是通过对训练集中的句子进行标记化而获得的。这个特征向量用于表示我们集合中的所有评论。基本上每个字都算是一个特征。因此,特征的数量等于词汇表中唯一单词的数量。现在,每一句话或每一篇评论都被视为一个样本或记录。如果该词存在于该样本中,则该词具有被视为特征的一些值,如果该词不存在,则该值为零。所以,如果‘今天是晴天’,和‘太阳从东方升起’是两句话。并且他们的包将具有 10 个单词,这将是特征向量大小。说矢量是:[‘它’,‘是’,‘一’,‘晴朗’,‘天’,‘太阳’,‘升起’,‘在’,‘东方’]。所以,第一句用[a,b,c,d,e,0,0,0,0]表示,a,b,c,d,e 是值,取决于我们使用的方案
因此,每个样本具有相同的特征集大小,这等于词汇表的大小。现在,词汇表基本上是由训练集中的单词组成的。训练集和测试集的所有样本都仅使用该词汇表进行转换。因此,测试样本中可能有一些词汇不在词汇表中,它们会被忽略。
它们形成非常稀疏的矩阵或特征集。类似于普通的分类问题,单词成为记录的特征,相应的标签成为目标值。如果我们考虑作为一个数据集,样本或评论将是行或记录,每个记录的特征集,或每个记录对应的特征列将等于词汇表的大小,其中每个单词将是一个特征。因此,它实际上就像一个常见的分类问题,特征的数量等于训练集中的不同标记。
这可以通过两种方式实现:
- 计数矢量器:这里是特定样本或评论中的单词计数。该单词的计数成为相应单词特征的值。如果 vocab 中的单词没有出现在样本中,则其值为 0。因此,基本上,如果训练集中有 10 个句子,这 10 个句子将被标记化以创建包。比方说,创建的包长度为 100 个单词。因此,我们将形成一个长度为 100 的向量。对于特定的样本,如果有 15 个单词,其中有一个单词在该样本中出现三次,则对于该样本,对应于该单词的值变为 3,依此类推。
- TF-IDF 矢量器:这是一种更好的方法。它计算两个东西术语频率和逆文档频率。 词频=该词在样本中出现的次数。 和 IDF = log(该词在样本中出现的次数/该词在整个文档中出现的次数) 。这有助于注意到一些差异,如单词“the”在几乎所有句子中出现的频率相同,而像“good”这样具有重要意义的特殊单词则不同。因此,将特定样本的每个单词的 TF 和 IDF 值相乘,以获得该样本的特征向量。
我们将在这里使用 scikit-learn 的特征提取库。但这里需要注意的一点是,这也可以使用 TensorFlow 的 Tokenizer 函数来完成。使用 tokenizer 主要有三种方法:
- binary:X = tokenizer . sequences _ to _ matrix(X,mode='binary '):在这种情况下,如果单词出现在样本中,则单词特征值为 1,否则为零。
- count:X = tokenizer . sequences _ to _ matrix(X,mode='count '):在这种情况下,值是一个单词在句子中出现的次数。
- TF-IDF:X = tokenizer . sequences _ to _ matrix(X,mode='tfidf '):在这种情况下,我们考虑样本中单词的 TF 和样本中单词相对于该单词在整个文档中的出现次数的 IDF。
计数矢量器:代码实现
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer()
vec.fit(X_train)
x_train=vec.transform(X_train)
x_test=vec.transform(X_test)
现在,如果我们注意到,向量只适合 X_train。这里是词汇形成的地方。所以词汇表只包含训练集中的单词。然后我们在训练集和测试集上进行转换。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
model = Sequential()
model.add(Dense(16, input_dim=x_train.shape[1], activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy',optimizer='Adam',metrics=['accuracy'])
这是我们计数矢量化方法的模型。我们可以看到,输入维度的大小等于每个样本的列数,也就是我们词汇表中的单词数。输出为 1,即情绪积极或消极。
该模型给出了 87.3%的准确度
TF-IDF 矢量器:代码实现
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer()
vec.fit(X_train)
x_train=vec.transform(X_train)
x_test=vec.transform(X_test)
现在,我们调用 TF-IDF 矢量器,数据被转换。这里我们使用逻辑回归模型。
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(x_train, Y_train)
该模型给出了 89.4%的准确度
第二种方法
单词嵌入:时间序列方法
在这种方法中,单词被单独表示为一个向量。在单词包的例子中,词汇表中的所有单词组成了一个向量。比方说,词汇表中有 100 个单词,因此,特定的单词将由大小为 100 的向量表示,其中对应于该单词的索引将等于 1,其他的将为 0。
因此,每个具有不同数量单词的样本将基本上具有不同数量的向量,因为每个单词等于一个向量。现在,为了给模型提供信息,我们需要让每个样本具有相同的维度,因此,需要填充以使每个样本中的单词数彼此相等。
基本上,在单词包或矢量器方法中,如果我们的总词汇中有 100 个单词,一个样本有 10 个单词,一个样本有 15 个单词,在矢量化之后,两个样本的大小都将是 100 个单词的数组,但在这里,对于 10 个单词,它将是(10 x 100),即,10 个单词中的每个单词的长度向量为 100,类似地,第 15 个单词的长度向量将是(15 x 100)。因此,我们需要找到最长的样本,并填充所有其他样本以匹配大小。
我们可以通过一些方式做到这一点:
一键编码:正如我们上面讨论的,它只是获取词汇表的大小,并在所有索引处创建一个大小为 0 的数组,而仅在单词索引处创建一个大小为 1 的数组。但是这些东西给我们提供的信息非常少,创建了一个非常稀疏的矩阵。
第二个选择是单词 embeddings。
一键编码器是一种非常硬的编码方法。这是一个非常高的维度和稀疏的数据量非常低。**嵌入是一种创建密集矢量表示的方式。**维度更低,有助于捕捉更多信息。它更像是通过词与词之间的相似性来捕捉它们之间的关系和相似性。例如,国王、王后、男人和女人会有一些关系。
比方说,我们有 10k 个单词被嵌入到 300 维的嵌入空间中。为此,我们声明嵌入层中的节点数=300。现在,10k 字的每个字都进入嵌入层。每个单词都将根据它们彼此之间的相似性被放置在一个 300 维的平面上,这是由几个因素决定的,比如单词出现的顺序。现在,被放置在 300 维平面中的单词将具有 300 长度的元组来表示它,该元组实际上是 300 维平面上的点的坐标。因此,这个 300 维的元组成为新的特征集或表示单词的向量。
因此,单词的向量从 10k 减少到 300。元组用作两个单词之间的特征向量,向量之间的余弦角表示两个单词之间的相似度。
我们可以通过两种方式做到这一点:
- 使用我们自己的嵌入
- 使用预先训练的嵌入
制作我们自己的嵌入
现在,对于嵌入,我们需要首先通过嵌入层发送每个样本,然后使用嵌入使它们变得密集。这些嵌入层查看单词是如何被使用的,也就是说,它试图查看两个单词是否总是一起出现或者被对比使用。在判断所有这些因素之后,该层将该单词放置在 n 维嵌入空间中的一个位置。
使用预先训练的嵌入式矩阵
我们可以使用预先训练的单词嵌入,如 google 的 word2vec 和 Standford 的 GloveText。他们在拥有数十亿个例子和单词的巨大语料库上接受训练。现在,他们有几十亿个单词,而我们只有一万个单词,所以用十亿个单词来训练我们的模型是非常低效的。我们只需要从预先训练的嵌入中选择出我们需要的单词的嵌入。
现在,这些嵌入是如何发现的?
对于 google 的 word2vec 实现,有两种方法:
- 滔滔不绝的话
- 跳格。
这两种算法实际上都使用具有单个隐藏层的神经网络来生成嵌入。
对于 CBOW ,单词的上下文,即所需单词之前和之后的单词,被馈送到神经网络,并且需要模型来预测单词。
对于 Skip-Gram ,给定单词,模型必须预测上下文单词。
在这两种情况下,单词的特征向量或编码向量被馈送到输入端。输出有一个 softmax 层,其节点数等于词汇大小,它给出了每个单词的预测百分比,即,它告诉所需单词是 softmax 层中的节点所表示的单词的概率。虽然我们实际上没有使用输出层。
我们去寻找隐藏层中产生的权重矩阵。**隐藏层的节点数等于嵌入维数。**因此,假设词汇表中有 10k 个单词,隐藏层中有 300 个节点,隐藏层中的每个节点在训练后都会有一个每个单词的 10k 维的权重数组。
因为神经网络单元工作在
y=f(w1x1+w2x2+………)。wnxn)
这里 x1,x2…xn 是单词,因此 n=词汇表中的单词数=10k。
所以对于 10k 的 x,会有 10k 的 w。现在,对于 1 个节点,有一个 10k 长的权重矩阵。对于 300 个组合,我们有一个 300 x 10k 重量的矩阵。现在,如果我们连接,我们将有 300 行和 10k 列。让我们转置矩阵。我们将得到 300 列和 10k 行。每行代表一个单词,300 个列值代表该单词的 300 个长度权重向量。
这个权重向量是所获得的长度为 300 的嵌入。
让我们看一下实现:
训练自己的嵌入
from keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=10000)
tokenizer.fit_on_texts(X_train)
Num_words 表示词汇的大小
x_train = tokenizer.texts_to_sequences(X_train)
x_test = tokenizer.texts_to_sequences(X_test)
-
tokenize.fit_on_text() →>根据词频创建词汇索引。例如,如果您有短语“我的狗与您的狗不同,我的狗更漂亮”,word_index[“狗”] = 0,word_index[“是”] = 1('狗’出现 3 次,'是’出现 2 次)
-
tokenize.text_to_sequence() →>将每个文本转换为一个整数序列。基本上,如果你有一个句子,它会给你句子中的每个单词分配一个整数。您可以访问 tokenizer.word_index()(返回一个字典)来验证分配给单词的整数。
vocab = len(tokenizer.word_index) + 1
from keras.preprocessing.sequence import pad_sequences
maxlen = 100
x_train = pad_sequences(x_train, padding='post', maxlen=maxlen)
x_test = pad_sequences(x_test, padding='post', maxlen=maxlen)
我们将所有句子填充到最大长度 100。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,Dense, Activation, MaxPool1D
from tensorflow.keras.optimizers import Adam
emb_dim=100
model= Sequential()
model.add(Embedding(input_dim=vocab, output_dim=emb_dim, input_length=maxlen))
model.add(MaxPool1D())
model.add(Dense(16,activation="relu"))
model.add(Dense(16,activation="relu"))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='Adam',loss='binary_crossentropy',metrics=['accuracy'])
最大池层用于挑选最能代表的特征以减少稀疏性。
模型摘要
该模型给出了 77.4%的准确度
使用预训练嵌入
我们将使用斯坦福的手套嵌入,它被训练了超过 60 亿个单词。
它有四个文件,每个文件有不同的嵌入空间,我们将使用 50d,这是一个 50 维的嵌入空间。
['glove.6B.100d.txt', 'glove.6B.200d.txt', 'glove.6B.50d.txt', 'glove.6B.300d.txt']
实施:
import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=10000)
tokenizer.fit_on_texts(X_train)
x_train = tokenizer.texts_to_sequences(X_train)
x_test = tokenizer.texts_to_sequences(X_test)
from keras.preprocessing.sequence import pad_sequences
maxlen=100
x_train = pad_sequences(x_train, padding='post', maxlen=maxlen)
x_test = pad_sequences(x_test, padding='post', maxlen=maxlen)
所以,让我们看看如何从给定的嵌入文件中提取我们需要的嵌入。
emb_dim=50
vocab=len(tokenizer.word_index)+1
emb_mat= np.zeros((vocab,emb_dim))
#Initializing a zero matrix for each word, they will be compared to have their final embeddingwith open(file_path+'glove.6B.50d.txt') as f:
for line in f:
word, *emb = line.split()
if word in tokenizer.word_index:
ind=tokenizer.word_index[word]
emb_mat[ind]=np.array(emb,dtype="float32")[:emb_dim]
这是一个任务的提取器,所以我们有一行嵌入和单词。所以,我们只是比较这些词来挑选出数据集中的索引。取向量,并把它们放在嵌入矩阵中对应于数据集中单词索引的索引处。
我们使用了*emb,因为嵌入矩阵的大小是可变的。
因此,我们这里有一个具有 10k 单词词汇表的特征集,每个单词由一个 50 长度的元组嵌入来表示,该元组嵌入是从手套嵌入中获得的。
接下来,我们建立我们的模型。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,Dense, Activation, MaxPool1D
from tensorflow.keras.optimizers import Adam
emb_dim=50
maxlen=100
model= Sequential()
model.add(Embedding(input_dim=vocab, output_dim=emb_dim,weights=[emb_mat], input_length=maxlen,trainable=False))
model.add(MaxPool1D())
model.add(Dense(16,activation="relu"))
model.add(Dense(16,activation="relu"))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='Adam',loss='binary_crossentropy',metrics=['accuracy'])
模型摘要
该模型给出了 67%的准确度,这可能是由于嵌入大小的减小。嵌入的大小越大,包含的信息就越多。
现在,这些方法是如何有益于单词袋模型的?正如我们所看到的,这些单词包模型只是看到了一个单词在文档中的表现,也就是说,我们可以知道这个单词出现的频率或这个单词出现的任何模式是什么?而这些方法也使用嵌入来考虑两个单词之间的关系。
卷积神经网络用于分类
在数学(特别是泛函分析)中,卷积是对两个函数(f 和 g)的数学运算,产生第三个函数,表示一个函数的形状如何被另一个函数修改。术语卷积既指结果函数,也指计算结果函数的过程。
因此,卷积最适合从图像的 2D 像素中提取特征值的特殊特征和行为。卷积层有一组内核,有助于从数据样本中提取几个重要特征。在文本分类的情况下,我们的特征矩阵是一维的。所以,这里用的是 Conv1D。基本上,它作为一个大小由用户决定的滑动窗口移动。我们选择了 5 个。
现在,最初在嵌入之后,我们得到 100 维嵌入。接下来使用 1D 卷积,我们试图使我们的特征集更小,并让特征集发现分类的最佳特征关系。max-pooling 层也有助于挑选具有最佳性能的特征或单词。
卷积层总是在嵌入层提供其嵌入的特征向量之后使用。
实施:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,Dense, Activation, MaxPool1D,Conv1D
from tensorflow.keras.optimizers import Adam
emb_dim=100
model= Sequential()
model.add(Embedding(input_dim=vocab, output_dim=emb_dim,
input_length=maxlen))
model.add(Conv1D(64, 5, activation='relu'))
model.add(MaxPool1D(5))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPool1D(5))
model.add(Dense(16,activation="relu"))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='Adam',loss='binary_crossentropy',metrics=['accuracy'])
模型摘要
这里我们训练了自己的 100 维嵌入矩阵。我们使用了两个卷积层,分别具有 64 个和 128 个内核滤波器。我们保持了滑动窗口=5。
该模型给出了 77%的测试准确度
使用递归神经网络进行分类
为什么选择递归神经网络?
到目前为止,我们一直试图从样本中的所有单词中一次提取一些特征。所以,它们都是非时间方法。现在,让我们看看一个人将如何判断一种情绪。他/她不仅会考虑使用了什么单词,而且人类还会考虑如何使用它们,即在什么上下文中,前面和后面的单词是什么?所以,到目前为止,我们只关注了所使用的单词,所以,现在让我们看看故事的另一部分。
因此,对于这一部分,我们需要一个递归神经网络来记忆我们的模型。如果我们想讲述某人的陈述,我们通常会逐字逐句地听完整个陈述,然后做出评论。这就是递归神经网络要完成的任务。它将逐个以时间方式查看每个单词,并尝试使用该单词的嵌入特征向量与上下文相关联。
我们知道 RNN 遭受消失和爆炸梯度问题我们将使用 LSTM。
LSTM 对两件事进行操作,一个是从前一个时间戳发送的隐藏状态,另一个是实际保持权重以抵消消失梯度效应的单元状态。
LSTM 层基本上有 4 个组成部分:一个遗忘门,一个输入门,一个单元状态和一个输出门。
现在,让我们谈谈 LSTM 的工作和数据流,因为我认为这将有助于展示特征向量实际上是如何形成的,以及它看起来是什么样子。
LSTM 方程
这些是 LSTM 运算的方程组。
**LSTM 为密集层提供了一个关于最后时间戳的特征集,以使用该特征集产生结果。**我们可以看到上面的方程是 LSTM 城门的方程。在这里,每个门单独充当一个神经网络。因此,在训练递归网络模型时,它们有自己的优化权重矩阵。使用这些权重矩阵,只有门学习它们的任务,例如要忘记哪些数据,以及需要将数据的哪一部分更新到单元状态。因此,门优化它们的权重矩阵,并根据它来决定操作。
现在,让我们看看它的用途。
假设我们有一个 100 维的向量空间。一批 16 个,每个样本长度= 10 个。并且每层中的节点数量= 64。
输入大小= batch_size *嵌入所以,这里是 16 x 100 矩阵= x(t)
时间戳 0 是每个样本或记录输入的第一个字。
x0 代表样本的第一个字,x1 代表第二个字,依此类推。因此,每次 1 个字来自 16 个样本,并且每个字由 100 长度的向量表示。因此,每个输入的大小是(16 x 100)。
前一隐藏状态(tiemstamp 0 的 0 向量)=批量大小 x 隐藏单元因此,这里是 16 x 64 矩阵。= h(t-1)
连接后,矩阵形成 h(t-1)= 16×64 和 x(t)= 16×100
所以 h(t-1) + x(t)矩阵被发送到所有的门 f(t),u(t)和 o(t)。
忘门
首先,隐藏状态的遗忘门权重矩阵 W{hf}的维数是 64×64,因为在隐藏状态中,对于时间戳(t-1)的 16 个字中的每一个,有来自 RNN 的 64 个节点中的每一个的 64 个值。
因此,实际上我们的隐藏状态矩阵的形状是(16 x 64):16 行记录,每条记录有 64 列或 64 个特征。
我们知道对于每个节点,
y=w1x1+w2x2+………wnxn
等式成立。
其中 x 是要素或列值。因此,对于网络的每个节点或单元,必须有一个维护的 64 个权重的数组,一个对应于一个 x。现在有 64 个这样的单元,所以总共有(64×64)个矩阵。
同样,现在对于 shape (16 x 100)的输入向量,100 列或 100 个特征中的每一列都有 16 行或 16 条记录。因此,一个隐藏单元的权重矩阵必须有 100 个值。总共有 64 个单元。所以,权重矩阵 W{xf}的维数为。(100 x 64)
所以在遗忘之门
根据等式 1。
f { t } = sigmoid((16 x 100)x(100 x 64)+(16 x 100)x(100 x 64))
F{t}=sigmoid (16 x 64)向量
Sigmoid 给出一个介于 0 和 1 之间的值。如果该值接近 0,则该值被忽略,否则在通过 F{t}后被添加到单元状态。
现在,单元状态也具有相同的维度(16×64 ),因为它也具有 16 个样本单词乘以 64 个节点的权重,所以它们可以被容易地相加。
更新门
接下来,是输入或更新门它决定数据应该进入的部分,这意味着递归神经网络的实际更新功能。它决定是否应该更新单元状态。
这些门的权重矩阵也与遗忘门的矩阵相同,在最后一个隐藏层中具有(64×64)个值,在输入中具有(100×64)个值。
W{hu}的尺寸为 64x 64,W{xu}的尺寸为 100 x 64。下标为“h”的权重矩阵与 h(t-1)部分相乘,相应地,下标为“x”的权重矩阵与级联的{x(t) +h(t-1)}向量的 x(t)部分相乘,该级联的{ x(t)+h(t-1)}向量是通过将先前隐藏的层与当前输入相加而获得的。
每个带 h 的权重矩阵都有维数(64×64),每个带 x 的权重矩阵都有维数(100×64)。
因此,输入门的结果也是 sigmoid(16 x 64)
根据等式 2,
u { t } = sigmoid((16 x 100)x(100 x 64)+(16 x 100)x(100 x 64))
U{t}=sigmoid (16 x 64)向量
这里要注意的一件事是还有一个 tanh 层。tanh 在这里压缩 1 到-1 之间的值来处理爆炸和消失渐变。因此,它基本上像正则化值一样工作,表示该时间步长上临时单元状态的值。乙状结肠是开关。
根据等式 3,计算临时单元状态。
C1 { t } = tanh((16 x 100)x(100 x 64)+(16 x 100)x(100 x 64))
C1 { t } =双曲正切(16 x 64)向量
C1{t}和 U{t}向量矩阵具有相同的维数。因此,可以应用点积。
因此,在这之后,所获得的向量被相乘以获得 1 个结果。
关于这一点需要注意的一点是,虽然权重矩阵的维数相同,但它们并不相同。它们属于不同的门,它们的值和优化都是不同的。
细胞状态
现在,在下一步中,单元步骤被更新
它基本上是一个简单的总和。
新的 c 或单元状态是通过从上一步+当前时间步的完成中移除不需要的信息而形成的。如根据等式 5 所示。
输出门
接下来是输出门。这决定了隐藏层的下一步应该是什么。
为此,新的单元状态通过一个 tanh 门,h(t-1) + x(t)通过另一个 sigmoid。两个结果相乘。那是下一个。
根据等式 4,输出门决定下一个隐藏层。
o { t } = sigmoid((16 x 100)x(100 x 64)+(16 x 100)x(100 x 64))
O{t}=sigmoid (16 x 64)向量
这里我们可以看到 O(t)的维数和 h(t-1)匹配。
现在,这些权重随着每个单词在每个时间步更新,在第 10 个单词或时间戳(我们的情况中的最终时间戳)之后,模型已经遍历了样本中的所有单词,因此我们得到大小为 16 x 64 的矩阵,这基本上是与每个样本相对应的 64 个内部节点的权重值。但是,我们看不到同样经过优化的门的权重矩阵。这一行中的 64 个值基本上代表了由 64 个节点逐个产生的批次中的单个样品的重量。
对于所有的样本,我们获得一个值。这些值充当密集图层执行操作的要素集。
实施:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,Dense, Activation, MaxPool1D, LSTM
from tensorflow.keras.optimizers import Adam
emb_dim=50
maxlen=100
model= Sequential()
model.add(Embedding(input_dim=vocab, output_dim=emb_dim,weights=[emb_mat], input_length=maxlen,trainable=False))
model.add(MaxPool1D())
model.add(LSTM(64, return_sequences = False))
model.add(Dense(16,activation="relu"))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='Adam',loss='binary_crossentropy',metrics=['accuracy'])
模型摘要
这里,我们使用了预先训练的单词嵌入。我们已经使用了 1 个具有 64 个隐藏单元节点的 LSTM 层。
该模型提供了 78%的准确度
LSTM-CNN 模型
它是一个混合模型,由 LSTM 层和 CNN 层组合而成。
LSTM 图层正在为原始输入生成新的编码。然后,LSTM 层的输出被送入卷积层,我们希望卷积层能够提取局部特征。最后,卷积层的输出将汇集到一个更小的维度,并最终输出为正标签或负标签。
结论
我们已经讨论了许多可以用于文本分类问题的方法,比如情感分析。
我希望这有所帮助。
Github 链接在这里是。
“地球上最难的数据科学锦标赛”的简单指南
好玩的数据科学?为了加密?为什么不两者都要?😀
来源:数字博客
更新*—2020 年 12 月 01 日:笔记本已根据新目标“Nomi”更新。目标名称现在是“目标”,而不是“目标一杉”*
更新*—2021 年 9 月:本笔记本使用的数据集现为旧版。一个新的超级海量数据集现已上线。只要稍加修改,代码仍然可以使用。*
告诉我密码:
确保你已经在number . ai上注册了,因为你需要设置你的 API 密匙来直接从 colab 提交。
第一次数字投稿指南
colab.research.google.com](https://colab.research.google.com/drive/1un9sQF5063VQ1UH13elYM269ZeYxHKFw?usp=sharing)
💡数字锦标赛问题
数字数据科学问题就像一个典型的监督机器学习问题,其中数据有几个输入特征和相应的标签(或目标)。我们的目标是使用各种技术学习从输入到目标的映射。我们通常将数据分成训练和验证两部分。而且大部分时间都花在清理数据上。
左图:训练数据样本。右图:样本提交
然而,数字数据是不同的。这是一个预测股票市场的问题,但它的独特之处在于数据被混淆并且已经被清洗!我们不知道哪一行对应哪一只股票。此外,每一行都被分成代表不同时间点的时代,但是只要它有结构,我们当然可以尝试从中学习和绘制模式。
Numerai 将这些经过清理的数据交给数据科学家,并要求他们为这些数据提供更好的估计。这些众包预测被用来建立一个元模型,并投资于世界各地的真实股票市场。奖励是基于你预测的质量和你的核磁共振赌注的数量。如果你的预测有助于获利,你就可以获得一定比例的股份,否则,你的股份就会被烧掉。这个赚/烧系统不断激励更好和独特的预测。因此,预测越准确和/或独特,回报越高。这就是它有趣和复杂的地方(最难的数据科学问题)。
让我们在 Google Colab 上解决这个问题。使用简单但非常好的技术— CatBoost 的端到端走查。我将在这里解释 colab 片段。如果您在与此平行的新标签中打开笔记本链接,将会非常有帮助。
管道➿
- 加载数据集(以及您将需要的一些操作)
- 定义模型
- 训练模特
- 验证
4.1 调整一些东西(回到步骤 1) - 预测并提交
5.1 观察 4 周以上的表现
设置 Colab
我们需要切换运行时来使用 GPU,方法是转到
Runtime -> Change runtime type -> GPU -> Save
Colab 预装了如此多的数据科学库。我们需要安装CatBoost
和numerapi
。
我们将在 colab 中设置您的管道,并使其足够灵活,以便在那里执行实验并使用 API 键提交预测。因此,一旦设置好按键并完成模型,您只需按下 colab 上的Run all
即可。
同样,确保你已经打开了这篇文章旁边的笔记本。
加载数据📊
使用 numerapi 下载数据并加载到内存中
锦标赛数据已经包含验证集(val1 和 val2 )。我们通常评估我们的模型对这个子集的预测,目标是在看不见的数据上表现良好。
定义和训练模型🤖⚙️
定义和训练模型
这可能是您进行大部分观察和调优的地方。您应该尝试其他类型的建模算法。
做出和评估预测📐
不要被这里这么多的代码弄得不知所措。这主要是帮助评估预测的样板代码。你可能不需要改变太多。然而,一旦你对锦标赛感到满意,你可能会想要添加更多的指标以获得更好的评估。
预测和验证
训练和验证集的评估结果
一旦你认为你的预测满足了你的目标,你可以使用你的秘钥在numerapi
的帮助下保存并上传它们。
提交预测📤
右上角的设置菜单
虽然您可以手动上传predictions.csv
,但我们将使用 API 来轻松提交。Numerai 允许你为不同的目的创建密钥,但是我们只为上传预测创建密钥。
用于创建密钥的自动化选项
不同用途的关键选项
要创建新的密钥,请转到
Settings -> Create API key -> select "Upload Predictions" -> Save
系统会提示您将密钥保存到安全的地方。
以下是提交预测的示例关键字。
提交预测的示例关键字
一个账号可以有 10 个型号。所以,在保持你的表现良好的模型不变的情况下,请随意试验新的技术。您可以使用numerapi
提交不同模型的预测。您可以在“设置”上方的“选项”中看到您的型号列表。你只需要复制model_id
并粘贴在这里。
获取模型密钥
提交预测
我的一个模型的统计,不是这个。
上传预测后,您将看到一些关于您提交的指标和信息。
从我的经验来看,在锦标赛中启动和运行需要几个提交。一旦你建立了你的工作流程,你需要做的就是在 Google colab 中按下Run all
。
Your predictions will be tested on live data and given scores,[CORR](https://docs.numer.ai/tournament/learn#scoring): Correlation between your predictions and live data
[Meta Model Contribution(MMC)](https://docs.numer.ai/tournament/metamodel-contribution): *An advanced staking option which incentivizes models that are unique in addition to high performing*You can [stake](https://docs.numer.ai/tournament/staking-and-payouts) your NMR on either CORR or CORR+MMC.
下一步是什么?💭
你可以做几件事来提高你的表现。你也会因为你预测的独特性而得到报酬。
- 玩弄数据
- 调整模型参数
- 变更模型架构
- 在 RocketChat 或论坛上提问
- 加入每周办公时间——详情请访问 RocketChat
从软件开发人员/工程师过渡到机器学习工程师的指南
如何从软件开发人员过渡到机器学习工程师的指南
图片由皮克斯拜的 Gerd Altmann 提供
他的博客面向任何对机器学习感兴趣的初学者或专业程序员/或学者。踏入机器学习工程领域,不需要依靠数据科学的工作经验。这个博客是为任何打算自学机器学习的人准备的。
我来告诉你我是如何开始成为一名机器学习工程师的旅程的。我有多年的 C++开发经验,对图像处理感兴趣。最近,机器学习已经被应用到许多现实世界的应用中。我很快就迷上了这种新的范式。从两年前开始,我一直在积极寻求更多的信息和知识,包括自学和做许多自己的项目。
作为一个热爱图像处理各方面的人,我从两年前开始决定探索深度学习。现在,我想发布这个资源来帮助其他人,那些像我一样对人工智能着迷的人,以及那些希望做同样事情的人。我希望这个博客能让他们更容易尝试我的方法。
目录
- 数据科学 vs 机器学习工程
- 机器学习技能
- 机器学习编程技能
- 其他活动
- 结论
- 资源
1。数据科学 vs 机器学习工程
理解数据科学家和机器学习工程师之间的差异至关重要。机器学习工程师角色专门针对机器学习,在工作描述中也可能被称为“软件工程师、机器学习”或“数据工程师”。
关于数据科学家和机器学习工程师之间的区别,有大量的在线资源,请务必查看:
作为机器学习工作流程的一部分,数据科学家利用统计分析来确定使用哪种机器学习方法。只有到那时,他们才开始原型制作和构建这些模型。
机器学习工程师通常会在建模过程之前和之后与数据科学家合作;构建数据管道,将数据输入模型。最后,设计一个服务于这些模型的工程系统,以确保模型的持续健康。
下图将解释机器学习项目中不同角色的交互:
机器学习项目中的不同角色
2.机器学习技能
2.1.机器学习理论
有很多资源可以学习机器学习理论。就我个人而言,我喜欢 T2 和吴恩达的课程。以下是我发现对理解机器学习有用的一些课程:
本课程提供了机器学习和统计模式识别的广泛介绍。这门课更深入算法的数学推导。
这是一门令人惊叹的课程,提供了机器学习的完整概述。作为一门应用机器学习课程,与 CS229 相比,它的数学性要低得多,在实践方面花费的时间更多。
在这门课程中,你将学习深度学习的基础,了解如何构建神经网络,并学习如何领导成功的机器学习项目。本课程不太关注数学,而是花更多时间教授应用机器学习算法的实用方法。这是这个列表中最实用的。
本课程详细介绍了深度学习架构的细节,重点是学习端到端模型,尤其是图像分类。它比 CS230 更深入地关注深度学习。
UC Berkeley stat-157 作者 Alex Smola:深度学习
这个课程是一个关于深度学习的很棒的课程,对最新的深度学习架构进行了解释。您将学习如何详细实现每个模型。你可以完全访问 GitHub 中的所有代码。
这些课程都很有用,你将从探索所有这些课程中受益。在每门课程中,你会学到不同的东西。这些课程为该领域提供了良好的基础。如果你想学习深度学习,你将需要学习机器学习和深度学习的基础。在你深入研究计算机视觉、自然语言处理、机器人技术或深度强化学习之前,你将需要这个基础。你还会从计算机视觉,自然语言处理,语音识别,甚至自动驾驶汽车上看到一点点每个专业化。这将帮助你决定你对这个领域的哪个领域最感兴趣。
2.2.深度学习框架
存在许多用于构建深度学习应用的库。最常见的有 TensorFlow,Keras,PyTorch。这三个库都是开源的。有许多资源可以用来了解关于这些框架的更多信息。我做了一个比较,你可以在我的 Github 中找到。
2.3.机器学习工作流程
机器学习工作流是从零开始构建机器学习项目所需的过程。这个过程可能因项目而异。然而,机器学习项目的核心通用步骤如下:
机器学习工作流程
3.机器学习编程技能
3.1.计算机编程语言
Python 是机器学习中最常用的语言。Python 的调试非常具有挑战性,因为它是基于动态数据类型的。对于那些来自强结构化语言(如 C++)的人来说,适应动态类型和脚本可能是一个挑战。
脚本语言旨在快速学习和编写,因为一般来说,它们要求简短的源代码。然而,随着应用程序的增长,您可能会面临一些困难。首先,你没有像 C++这样的编译器来捕捉你的错误。此外,所有的类型都是动态的,所以当你的程序变大时,你可能很难跟踪并找出变量的类型。这可能会使你的代码变得容易出错,尤其是如果它设计得不好的话。
使用 python 包管理器是一个很好的实践。全局安装的问题是,对于给定的 Python 解释器,一次只能安装一个版本的包。当您的应用程序需要同一库的不同版本时,这可能是一个问题。此外,升级库可能会破坏应用程序
有许多 python 包管理器,但是在数据科学工作中最常用的是 Anaconda。Anaconda 简化了包的管理和部署。它只需一次安装就可以带来许多数据科学和机器学习中使用的工具,因此它非常适合进行简短的设置。
Anaconda 的另一个重要特性是 conda,它是 Anaconda 包管理器。您可以使用 conda 安装许多库。此外,在 Anaconda 中,我们可以创建一个虚拟环境,这有助于隔离不同的库和版本。如果他们使用不同的 Python 库,那么为每个项目创建一个虚拟环境是一个好主意。
3.2。Python 库
Python 的标准库非常大,您的项目可能需要很多。这些库包含了很多用 c 编写的模块,最常见的你应该知道的有 panda、numpy、matplotlib、opencv-python、scipy、scikit-learn、tensorboard。
如果您想了解更多,我会在参考资料部分留下有用的链接供您访问。
3.3。Python 集成开发环境
有许多集成开发环境(ide)可供您选择来开发您的软件。用于机器学习的常见 ide 有 Jupyter Notebook 、 PyCharm 。这些 ide 使得机器学习的编程更加容易。
如果你正在构思一个想法或制作一个简单的功能,Juyter 笔记本是很容易的。我发现在 Jupyter Notebook 中格式化我的代码或调试我的代码并不容易。
PyCharm 是 JetBrains 公司开发的一款开源软件,JetBrains 公司以其名为 IntelliJ IDEA 的 Java IDE 而闻名。PyCharm IDE 的一些有趣特性包括一个代码编辑器,一个非常强大的带有图形界面和错误高亮的调试器。它内置了对 Git、SVN 和 Mercurial 的支持。
4。其他活动
4.1。免费提供的博客
有许多博客提供关于人工智能和机器学习的信息。其中一些博客属于谷歌或 Bair Blog 等知名公司。但是如果你正在寻找简单的例子和指南,你可以参考以下三个博客;走向数据科学、中等和机器学习精通。
4.2。播客
播客是获取新技术和市场方向信息的绝佳资源。下面是我最喜欢的两个播客:实用 AI 和今日 AI。
4.3。练习机器学习
send ex Youtube频道包含了很多机器学习和深度学习的实际应用。他专注于机器学习和深度学习的实现,因此他不会深入每个算法背后的理论。他用每个人都能听懂的非常简单的语言一步一步地解释一切。你可以从他的博客中获得所有的代码。
我使用 Keras 和 TensorFlow 做了一个简单的狗和猫的分类项目,可以帮助你开始建立深度学习项目。您还将熟悉使用 TensorFlow 数据集构建数据管道来对猫和狗的图像进行分类。你可以访问我的 GitHub 中的代码。
我做了一个小的回归项目,演示了如何建立一个简单的线性回归。这段代码也可以从我的 GitHub 中获得。
4.4。参加比赛
另一个学习新技术和建立人际网络的好地方是 meetup。参加聚会会非常有用。黑客马拉松是练习和提高你的 python 和深度学习技能的另一个好方法。黑客马拉松通常是为期一天的比赛,在比赛中你会收到真实的数据和挑战,你需要在一天结束前解决这些问题。
现在,我们知道如果我们需要建立一个深度学习项目,我们需要数据。要开始一个深度学习项目,你首先需要有一个图像数据集。构建数据集可能非常具有挑战性,甚至是不可能的。幸运的是 Kaggle 为我们提供了许多免费的数据集用于此目的。
如果你达到了需要更大挑战的地步,你可以参加 AI 比赛。每年,许多公司,如人工智能实验室、微软、Kaggle 和其他公司都会举办你可能会参加的竞赛。
5.结论
希望这篇文章对那些想知道如何成为一名机器学习工程师的人有所帮助。
以下是一些你可以自己研究的想法:
- 测试机器学习系统
- 尝试了解机器学习部署
- 尝试了解 Docker
6.资源
l \从科里斯查费 youtube 频道和send exYouTube 频道学习 python:这些频道是学习 Python、机器学习、深度学习和自然语言处理的丰富资源
Conda 备忘单:该备忘单总结了 Conda 命令行指令的常用方法,以供快速参考
PyCharm 插件库:插件非常有用,可以帮助你轻松扩展 PyCharm 的功能
更多推荐
所有评论(0)