TowardsDataScience 博客中文翻译 2020(九百四十九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

通过 Python 使用 MongoDB

原文:https://towardsdatascience.com/using-mongodb-with-python-bcb26bf25d5d?source=collection_archive---------13-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Unsplash 上的 h heyerlein 拍摄

在这篇文章中,我们将学习如何在 Python 中使用 MongoDB。MongoDB 是一个非常快速灵活的 NoSQL 数据库。它非常受欢迎,应用广泛。

在潜水之前,让我们先来学习三个我们会经常用到的术语。

  • 文档: MongoDB 是一个文档数据库,这意味着集合中的每条记录都是一个文档。
  • **集合:**一个集合可以存储多个文档。
  • 数据库:数据库保存文档的集合。

我们来深入探讨一下。

目录

1.安装 MongoDB

我将使用 MongoDB Atlas 免费层,这样您就不需要在您的计算机上安装 MongoDB,这样我们将只关注 MongoDB 和 Python。你可以从这里得到。在那里创建一个集群并设置你的账户。

建立帐户后,不要忘记将您当前的 IP 地址添加到白名单中,并创建一个 MongoDB 用户,否则您将无法访问您的集群。

安装 PyMongo

我们需要安装pymongodnspython。我们需要dnspython来连接mongo+srv协议。要安装两者,可以使用下面这个命令。

pip install pymongo[srv]

2.连接到您的集群

设置好账户后,点击控制面板中的connect按钮,然后点击connect your application,再选择Python driver。您将在那里看到您的连接字符串,复制它。

不要忘记将admin<password>占位符替换为您在设置用户时创建的用户名和密码。请注意,此用户不是您注册时的用户,而是您在创建集群后创建的用户。

我将使用环境变量,因为我不想公开我的用户信息。您可以简单地将user变量分配给用户名,将password变量分配给密码。

3.创建数据库

连接到您的集群后,您可以作为client的属性访问您的数据库。所以当我们说client.test时,我们基本上是访问一个名为test的数据库。如果我们还没有test数据库,它将在插入一个文档到数据库后被创建。我们将在本节中看到如何做到这一点。

立正!如果您的数据库名称是test-db,您将不能使用类似client.test-db的属性样式。你应该使用字典风格。client[‘test-db’]会起作用。

让我们看看我们的test数据库是否存在。运行下面的代码后,您期望哪个输出?输出将是test database was not created.,因为我们还没有向test数据库插入任何文档。一旦我们将文档插入到数据库中,数据库就会被创建。

在数据库中创建集合

让我们创建一个名为col的集合,看看会发生什么。检查输出,我们没有名为test的数据库和名为col的集合。让我们插入一个文档,看看会发生什么。

插入文档

首先,我们需要创建一个user字典来将文档插入到集合中。user字典的关键字将是文档的字段,user字典的元素将是文档中该字段的值。

我们将把user插入到col集合中,我们可以用insert_one的方法来完成。插入这个文档后,您可以获得它的 id。insert_user.inserted_id是 bson 对象,不是常规字符串。

如果您没有自己指定_id字段,它会自动添加到您的文档中。

你可以很容易地在你的面板中创建一个数据库和集合,但我也想用这种方式来展示。

4.插入文档

到目前为止一切顺利。我们已经创建了我们的test数据库,我们可以使用client的属性访问风格来访问它。现在我们可以在这个数据库上创建一个名为blog的集合,并将文档插入到这个集合中。

我们首先选择test数据库,然后db指向该对象。然后我们创建一个blog集合,blog_collection指向那个对象。

我们可以在插入文档后检索它的 id。请参见我们文档第 13 行插入的 id。

在将第一个文档插入到blog集合之后,博客集合就在我们的数据库test上创建了。

我想自己指定文档的 id,可以吗?

是的,你可以!_id会自动确定,除非你亲自动手。我自己来,把我的_id设为1

要非常小心!如果您插入另一个带有1_id的文档,id 在文档中应该是唯一的。您将得到一个重复的密钥错误。身份证就像你的公民身份证,他们应该是唯一的,以被正确识别。

5.插入多个文档

我们可以插入多个文档,看看如何看到代码片段。我希望你在这个片段中关注 3 件事。

  • 首先,我们需要传递一个字典列表来插入多个文档,其中每个字典代表一个文档,检查第 10 行的posts变量。
  • 其次,看看我们的文档有多灵活,不需要模式!每个文档都不一样!你可以在 MongoDB 中做到,没有什么可以阻止你去做!
  • 第三,看插入多个文档和一个文档的区别,我们用insert_many代替insert_one,用inserted_ids代替inserted_id

我们将把posts变量作为第一个参数传递给insert_many方法。

6.更新一个文档

我们可以更新数据库中的文档。我们将使用update_one方法对我们的数据库执行一次更新。让我们回顾一下所有的步骤,并很好地理解如何去做。

  • 首先,我在第 9 行检索匹配我们的query过滤器的文档。我这样做是因为我想在更新之前/之后给你看。
  • 第二,我们准备一本字典,然后new_document指向它。重点看那本字典,‘**$set**’{'content': 'I love MongoDB and Python', 'title': 'Updated Post'}。我们这里基本讲的是设置 contentI love MongoDB and Python``titleUpdated Post
  • 最后,我们检索更新后的文档,看看它是否有效。看看updated_document的输出,我们的修改被应用了。Id 和其他我们没改的还是一样,只有contenttitle按照我们的本意改了。

update_one方法的第一个参数是你的过滤器,第二个参数是你要应用的修改和upsert。如果您将upsert设置为True,如果没有符合您的过滤器的文件,它将插入一个新文件。默认是False

如果有多个文档与您的过滤器匹配,将更新第一个匹配项。

7.更新多个文档

您博客中的一个用户已经更改了用户名,因此,您想要更新他的所有文档,以便将他的旧用户名更改为新用户名。我们将使用update_many方法更新多个文档。

8.获取一个文档

目前我们已经填充了数据库。现在我们想搜索我们的数据库,并检索与我们的查询相匹配的文档。

我们将使用find_one方法。我们将把{‘title’: ‘Second’}字典作为第一个参数传递给这个方法。通过传递这个字典,我们基本上告诉我们要从blog集合和test数据库中检索一个标题为Second的文档。我们只使用title进行过滤,但是我们可以向我们的{‘title’: ‘Second’}字典中添加更多像‘user’: ‘halilylm’这样的字段。

注意document是一个字典,所以您可以使用 get 函数来检索第 12 行的内容。

如果我们有不止一个带有Secondtitle的文档怎么办?向下滚动到下一个例子,看看会发生什么!

如果有多个文档匹配我们的查询,我们只检索第一个匹配。请参见下面的示例。有多个文档与我们的查询匹配,我们只得到第一个匹配项。

9.通过 id 获取文档

让我们假设,您创建了一个博客,并希望按 id 显示一篇文章。URL 是/blog/posts/5ed0254d97e5367d20c22e63,而5ed0254d97e5367d20c22e63是帖子的 id。因为这是一个普通的字符串,如果你用它作为_id,你将得到None。因为这是一个普通的字符串,而不是 bson 对象。您需要将这个字符串对象转换为 bson 对象。我假设这里的 id 是自动添加的,而不是你自己指定的。

您只需要导入ObjectId并将插入的 id 作为第一个参数传递给它。

我还想排除titlecontent,我可以传递一个我想排除的字段字典,并将字段设置为0作为find_one方法的第二个参数。看输出,titlecontent排除在外。

10.获取多个文档

您也可以一次获得多个文档。让我们尝试检索具有halilylmuser字段的所有文档

11.更复杂的查询

查询并不总是简单的。如果你在你的博客中建立一个搜索系统,你可能想要使用更复杂的查询。我的意思是,当一个用户搜索一个关键字时,你也希望显示与该关键字相关的帖子,而不仅仅是与用户完全匹配的帖子。

假设我们想要显示文档以及这些文档中以I love开头的content字段。你会怎么做?我们将使用正则表达式来实现这一点。我将传递一个字典来过滤我们想要的结果。这本字典将作为第一个参数传递给find方法。

我还想排除idpost_dateuser。我将把字典作为第二个参数传递给find方法。

看到输出,我们得到所有匹配我们的查询的文档。

12.删除一个文档

我们将使用delete_one方法从数据库中删除一个文档。

我们想要删除一个带有First posttitle的文档,因此我们需要将{'title': ‘First post'} dictionary 作为第一个参数传递给delete_one方法来定义我们到底想要删除哪个文档。

您可以使用deleted_count属性访问方式检查删除了多少个文档。第 9 行的输出是 1,因此一个与我们的查询匹配的文档如预期的那样被删除。

如果有多个文档匹配该查询,该怎么办?答案是只有第一个匹配会被删除。

13.删除多个文档

我们可以一次删除多个文档。假设halilylm用户已经删除了他的账户。我们想删除所有属于他的帖子。我们将使用delete_many()方法来执行此操作。

立正!如果您将空字典传递给delete_many()方法,它将从该集合中删除所有文档!非常小心!!!

14.限制性的

假设我们的查询与2 posts匹配,我们希望将结果限制为仅与1 post匹配。我们可以限制从数据库中获取的文档数量。为此,我们将使用limit方法。只需将您想要从我们的数据库中检索多少个文档传递给limit方法

看看输出,我们只得到一个文档。输出更长,因为这次我没有排除任何字段。

15.整理

我们将使用sort方法按照title字段对结果进行排序。

将字段名作为第一个参数传递给sort方法。将1–1作为第二个参数传递给sort方法。然而,你不必通过1来指定方向,因为默认是升序。

排序(‘标题’,1)升序(默认)
排序(‘标题’,-1)降序

我们先按升序排序,排除idpost_dateusercategory字段。

注意,一个空字典向find方法传递了第一个参数。这意味着你不想应用任何过滤器。第二个参数是要排除的字段的字典。

对于降序,只需将-1放在sort方法的第二个参数中。

结论

我希望您喜欢这篇文章,并且很好地理解了如何在 Python 中使用 MongoDB。敬请关注未来的作品。

利用地铁数据安置志愿者——深入研究数据科学

原文:https://towardsdatascience.com/using-mta-turnstile-data-to-place-optimize-placement-of-volunteers-and-start-a-career-in-data-1bece6a33f56?source=collection_archive---------41-----------------------

介绍

我最近开始了一个 Metis 数据科学训练营,加入了大约 55 名其他有抱负的数据科学家,为期 12 周,充满了编码、讲座、挑战和项目。我有生物学背景。我在一月份完成了旧金山州立大学海洋生物学的硕士学位。我进入 2020 年时,计划做一名实验室技术员或其他一些低级别的移液器猴子,同时我找到了一条具体的职业道路,但新冠肺炎全球疫情完全打乱了这个计划。作为一个住在新城市(俄勒冈州波特兰市)的 26 岁年轻人,我发现自己在寻找一些职业方向和日常目标。我对编码产生了浓厚的兴趣,并很快开始利用这些技能研究职业。我很快找到了 Metis,正式开始了我的数据科学之旅。

该项目

在这里,我描述了我在 Metis 课程中的第一个项目的经历——也是我的第一个数据科学项目(查看回购)。任务是分析纽约市 MTA 地铁数据,以便安排志愿者招募客人参加虚构公司 WomenTechWomenYes 举办的年度晚会。

许多人已经分析了这个数据集(google it),当我着手这个项目以及一个新的博客时,我想知道自己:我能贡献什么?作为一名数据科学新手,我怎么能希望提供其他更有经验的数据科学家错过的启示呢?这似乎不太可能。相反,我想通过我自己的视角与你分享这个项目:作为一个全新的、天真无邪的数据科学家,他希望深入到数据科学的本质、数学和 Python 驱动的世界中。

通过分享我在这个项目中的思考过程,我希望能阐明为什么我认为数据科学如此有用——如此彻头彻尾的。当我分享我的分析的代码片段和输出时,我希望其他新兴的数据科学家能够跟随并受到启发,去挖掘许多许多等待分析的可用数据集。因为归根结底,我的目标是让更多的人对数据科学感兴趣,数据科学是一套广泛适用的技能,以解决问题和帮助人们为中心。让我们开始吧。

该项目的第一步是找到并导入数据集,以便我们可以进行一些 EDA(首字母缩写词在数据科学中非常重要)。

从 MTA 网站加载了数周的十字转门数据:

df_1 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190323.txt')
df_2 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190330.txt')
df_3 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190406.txt').
.
.df_12 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190608.txt')
df_13 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190615.txt')
df_14 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190622.txt')Mta_2019_df_input_1 = pd.concat([df_1, df_2, df_3, df_4, df_5, df_6, df_7, df_8, df_9, df_10])mta_df = pd.concat([Mta_2019_df_input_1, df_10, df_11, df_12, df_13, df_14])

由此产生的数据帧非常壮观:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我一点也不知道清理和使用这个数据集会有多么困难!但这是有趣的部分。

清理的第一步是添加一些列并清理列名:

mta_df['DATETIME'] = pd.to_datetime(mta_df['DATE'] + ' ' + mta_df[“TIME”], format=”%m/**%d**/%Y %H:%M:%S”)mta_df['TURNSTILE'] = mta_df['C/A'] + ' ' + mta_df['UNIT'] + ' ' + mta_df['SCP'] + ' ' + mta_df['STATION']

然后删除一些重复项:

mta_df.drop_duplicates(subset=['C/A', 'UNIT', 'SCP', 'STATION', 'DATETIME'], inplace=**True**)

然后我们制作了一个新的框架,它由四个属性组成,这四个属性共同代表了一个独特的十字转门:C/A、Unit、SCP 和 Station(每一个都是 MTA 十字转门的标识符)。

mta_daily = (mta_df.groupby(['C/A', 'UNIT', 'SCP', 'STATION', 'DATE'],as_index=**False**).ENTRIES.first())mta_daily.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

清理工作完成后,我们准备开始分析,这将有助于我们实现目标:通过瞄准最繁忙的车站和十字转门,最大限度地发挥志愿者的影响力。但是我们看到了另一个问题:十字转门的入口和出口是累积的,而不是代表自上次观察以来的乘客(每四小时观察一次)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我不知道如何解决这个问题,并得到我们的进入和退出总数在尼斯四个小时的增量。这对我来说是这个过程中的一个重要时刻。我甚至对这个简单的问题感到不知所措,觉得自己缺乏真正成为数据科学家的技能(有人告诉我,冒名顶替综合症是许多技术领域的常见疾病)。我感到很沮丧。

然而,我并不是独自进行这项分析:这毕竟是一个团队项目,而且最重要的是,我们有愿意提供帮助的导师。经过一天左右的努力,我们的一位导师向我们展示了一个真正优雅的解决方案,它允许我们将累积的进出变成每日的进出:

# Adding previous date and previous entries, so that we can look at the deltas:mta_daily[["PREV_DATE", "PREV_ENTRIES"]] = (mta_daily.groupby(["C/A", "UNIT", "SCP", "STATION"])["DATE","ENTRIES"].apply(lambda grp: grp.shift(1)))# Dropping top row of NaN's:
mta_daily.dropna(subset=["PREV_DATE"], axis=0, inplace=True)# Where is the counter going the wrong way?
mta_daily[mta_daily["ENTRIES"] < mta_daily["PREV_ENTRIES"]].head()# Fixing counter
def get_daily_counts(row, max_counter):
    counter = row["ENTRIES"] - row["PREV_ENTRIES"]
    if counter < 0:
        # Counter reversed?
        counter = -counter
    if counter > max_counter:
        # Counter reset to 0? 
        counter = min(row["ENTRIES"], row["PREV_ENTRIES"])
    if counter > max_counter:
        # Still too big?
        return 0
    return counter# finally adding our daily entries column!
mta_daily["DAILY_ENTRIES"] = mta_daily.apply(get_daily_counts, axis=1, max_counter=40000)mta_daily.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

耶!现在我们有了一个每日条目的专栏(我们只关注项目剩余部分的条目)。

除了有助于我们继续分析之外,这种合作干预揭示了一个极其重要的启示:**在解决这个问题时,我不是一个人。**这是一个团队项目,我们的教练已经表明他会在那里帮忙。

事后看来,这似乎是一个显而易见的观察。Metis 强调项目的协作性,我确实在这个项目上与一个小组一起工作。但我想强调这一系列事件,因为我真的觉得这是一个“水平上升”的时刻。我从来没有合作编写过代码,在进入一个新的程序时,我想向自己和他人证明我有能力去执行和竞争。然而,我觉得更多的人应该牢记一起工作和寻求帮助的教训。

带着一种“一切都会好起来”的新感觉,我们继续分析。我们现在有每个车站的每日入口和出口,我们想找到最繁忙的车站。我们首先按电台对数据帧进行分组,并按每日条目进行排序,以查看前 10 名:

top10_stations = \
    (stations_daily.groupby(['STATION'])['DAILY_ENTRIES'].sum()
                   .reset_index()
                   .sort_values(by='DAILY_ENTRIES',ascending=**False**) 
                   .STATION.head(10)) 

然后,我们提取了数据帧中属于前 10 名的部分:

station_daily_top10 = \
    stations_daily[stations_daily['STATION'].isin(top10_stations)].sort_values(by = 'DAILY_ENTRIES', ascending = **False**)

最后在 Seaborn 用标准的箱线图绘制出来:

sns.boxplot(x='DAILY_ENTRIES', y='STATION', data=station_daily_top10, order = top10_stations, \
            showfliers = **False**).set_title('10 Busiest Subway Stations', size = 15);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

厉害!通过几行代码,我们从一个庞大、杂乱的数据集中提取了对我们的客户有价值的第一条信息:纽约市 10 个最繁忙的地铁站!接下来,我们使用 geo_pandas 在地图上显示了最繁忙的车站:

top10_stations_geo = combined_df[combined_df['STATION'].isin(top10_stations)]fig, ax = plt.subplots(1, figsize=(10, 10))
districts_geo.plot(ax=ax)
top10_stations_geo.plot(ax=ax, column = top10_stations_geo['DAILY_ENTRIES'], \
                        legend=True, cmap = 'hot', markersize = 50)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

聚焦曼哈顿:

manhattan = districts_geo[districts_geo['boro_cd'].astype(int) <200]
fig, ax = plt.subplots(1, figsize=(20, 20))
manhattan.plot(ax=ax)
top10_stations_geo.plot(ax=ax, column = top10_stations_geo['DAILY_ENTRIES'], \
                        legend=True, cmap = 'hot', markersize = 80)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

漂亮!根据我们的分析,我们现在可以向客户展示最佳目标站的准确位置。

在这一点上,我们几乎完成了,我觉得完成了。该项目进展顺利,我们几乎准备好交付最终演示。

我们项目的最后一个方面是将人口统计数据整合到我们的分析和推荐中。首先,我们阅读从 data.cccnewyork.org 获得的人口统计数据,这是一个专注于追踪纽约市收入的网站。我们把注意力集中在纽约最富裕社区的名单上,理由是越富裕的人越有可能在晚会上捐款。

bougie_df = pd.read_csv('Income_Stations_by_Neighborhood.csv')
bougie_df.head()

然后,我们编写了一个函数,将人口统计数据框架中的电台名称与“电台 _ 每日”框架中的电台名称进行匹配。

def get_station_name(row, col_name, station_names, check_length = False):
    """
    Takes in a row of a dataframe and matches the value of the column labeled by 
    col_name with the values in station_names
    """
    row_name = row[col_name]
    row_name = re.sub('th', '', row_name)
    row_name = re.sub('rd', '', row_name)
    row_name = re.sub('nd', '', row_name)
    row_name = re.sub('-', '', row_name).upper()
    similarity = 0
    similar_name = ''
    for name in station_names:
        if check_length:
            if len(name) < 14:
                name += "".join(['-' for i in range(len(name), 14)])
        ratio = SequenceMatcher(None, row_name, name).ratio()
        if ratio > similarity:
            similarity = ratio
            similar_name = name.strip('-')
    return similar_name

我们将这些站名添加到我们的人口统计数据框架中:

bougie_df['MTA_name'] = bougie_df.apply(get_station_name, axis = 1, col_name = "MAP NAME", \
                                        station_names = stations_daily['STATION'].unique())bougie_df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后我们交叉参考这些车站,找出富裕社区中最繁忙的车站:

bougie_list = bougie_df['MTA_name']bougie_stations = \
    stations_daily[stations_daily['STATION'].isin(bougie_list)].sort_values(by = 'DAILY_ENTRIES', ascending = False)
bougie_top10 = \
    (bougie_stations.groupby(['STATION'])['DAILY_ENTRIES'].sum()
                   .reset_index()
                   .sort_values(by='DAILY_ENTRIES',ascending=False) 
                   .STATION.head(10))

最后,我们绘制了这些社区中最繁忙的 10 个车站:

sns.boxplot(x='DAILY_ENTRIES', y='STATION', \
            data=bougie_stations[bougie_stations['STATION'].isin(bougie_top10)], \
            order = bougie_top10, showfliers = False);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

原来如此!最后一个交付给我们客户的东西:纽约最富裕社区的十大最繁忙地铁站。

最后的想法

在完成了我的第一个数据科学项目之后,我感觉自己已经跨越了一个里程碑。不是戏剧性的,但在 Metis 的筹备和第一周期间,我有无数次合理地不确定自己是否能做到,我的数学、编码和分析技能是否达到 Metis 为学生设定的高标准。完成这个项目后(回购!),我感觉我可以在数据科学方面取得成功,我真的很兴奋能继续这个训练营。

我最近开始着手我的第二个项目(我很快会写博客,别担心),从第一个项目中获得的信心支撑着我。如果您没有从这篇博客文章中获得任何其他东西,我希望您可以将我的数据科学之旅的开始视为灵感,以真正涉足您正在做的任何事情,并知道您周围总有人可以帮助和支持您!下次见。

使用朴素贝叶斯从零开始创建国际象棋人工智能

原文:https://towardsdatascience.com/using-naive-bayes-to-create-a-chess-ai-from-scratch-e35bc64ef109?source=collection_archive---------53-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

皮奥特·马科夫斯基在 Unsplash 上的照片

国际象棋一直被认为是智力和智力的测试。1996 年,在国际象棋大师加里·卡斯帕罗夫和 IBM 的深蓝之战后,它也成为了人工智能的一块基石。因为它的重要性,我认为尝试创造一个有能力的象棋人工智能会很有趣。

为了实现创建一个有能力的国际象棋人工智能的目标,我开发了一个朴素贝叶斯分类器,使用概率来寻找最佳可能的移动。

我使用的数据集有超过 20,000 个游戏。可惜我再也找不到源头了。如果你正在寻找数据集,你可以联系我在 victorwtsim@gmail.com。

朴素贝叶斯分类器:

朴素贝叶斯分类器依赖于使用从数据集收集的概率来做出最佳决策。对于国际象棋,这是如何实现的?以下是一个逐步指南:

第一步:统计每一步棋在输赢游戏中出现的次数。

第二步:将每一个计数值,除以输局的总棋数和赢局的总棋数,变成一个概率。

第三步:利用贝叶斯定理计算出获胜概率最高的招式。

代码:

第一步:在一场输和赢的游戏中,每出现一个招式,就数一次。

from pandas import read_csv
import chess
file = '/Users/XXXXXXX/Desktop/Files/Data/chess.csv'
csv = read_csv(file)outcomes = list(csv['winner'])
moves = list(csv['moves'])
for i in range(len(moves)):
    moves[i] = moves[i].split()
white_moves = 0
black_moves = 0

在进入实际程序之前,这些是我用来访问我的数据的先决条件。如果您可以访问数据集,您可以复制它。

def initialize_dictionaries():
    white = {}
    black = {}
    all_moves = []
    for game in moves:
        for move in game:
            if not(move in all_moves):
                all_moves.append(move)
    for move in all_moves:
        white[move] = 1
        black[move] = 1
    return white,blackdef initialize_data(white,black):
    white_moves = 0
    black_moves = 0
    for outcome in outcomes:
        index = outcomes.index(outcome)
        if outcome == 'white':
            work_dict = white
            white_moves += len(moves[index])
            ip2 = 0
        elif outcome == 'black':
            work_dict = black
            black_moves += len(moves[index])
            ip2 = 1
        for move in moves[index]:
            if moves[index].index(move) % 2 == ip2:
                if move in work_dict:
                    work_dict[move] += 1
    return white,black,white_moves,black_moveswhite,black = initialize_dictionaries()
white,black,white_moves,black_moves = initialize_data(white,black)

这两个函数查看数据,将游戏的结果与移动匹配,并相应地添加每个移动在输赢游戏中出现的次数。我以 Python 字典的形式存储数据,以支持基于文本的索引。

第二步:将每一个计数值除以输局的总棋数和赢局的总棋数,变成一个概率。

def to_probability(dictionary,length):
    for term in dictionary:
        dictionary[term] = dictionary[term]/length
    return dictionary
white = to_probability(white,white_moves)
black = to_probability(black,black_moves)

这个函数非常简单,几乎不需要解释:用一个移动出现的次数除以移动的总数,得到一个概率。

第三步:利用贝叶斯定理计算出获胜概率最高的招式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

贝叶斯定理指出,假设 B 发生了,A 发生的概率是 B 发生的概率,当 A 发生时,乘以 A 发生的概率,除以 B 的概率。

让我们用赢来代替 A,用移动 X 来代替 B。赢的概率,给定移动 X 被使用,可以计算为:移动 X 在赢的配置中出现多少次,乘以总的赢的概率,再乘以移动 X 被使用的概率。

PA = outcomes.count('white')/len(outcomes)
PB = 1
PBgiveA = white[move]/outcomes.count('white')
whitePAgiveB = (PBgiveA * PA) / PB

这里的代码是这个概念的应用,给出了白棋赢的概率。请注意,移动 X 的概率是 1,如果我们想移动,移动有 100%的机会发生。

完整的功能是:

def calculate_probability(side,board,white,black,white_moves,black_moves):
    len_white = white_moves
    len_black = black_moves
    if side == True:
        color = 'white'
    else:
        color = 'black'
    legal_moves = str(board.legal_moves)[36:-2].replace(',','').split()
    probs = []
    for move in legal_moves:
        if not(move in white):
            len_white += 1
            white[move] = 1/len_white
        PA = outcomes.count('white')/len(outcomes)
        PB = 1
        PBgiveA = white[move]/outcomes.count('white')
        whitePAgiveB = (PBgiveA * PA) / PB
        if not(move in black):
            len_black += 1
            black[move] = 1/len_black
        PA = outcomes.count('black')/len(outcomes)
        PB = 1
        PBgiveA = black[move]/outcomes.count('black')
        blackPAgiveB = (PBgiveA * PA) / PB
        if color == 'white':
            probs.append(whitePAgiveB-blackPAgiveB)
        elif color == 'black':
            probs.append(blackPAgiveB-whitePAgiveB)
    final_move = legal_moves[legal_moves.index(max(legal_moves))]
    return white_moves,black_moves,final_move

结果:

我试着用这个算法和它自己玩。它对开局和理想位置有很好的感觉(比如确保中路),但在捕捉棋子时有问题。

我认为这是因为朴素贝叶斯分类器只知道合法的移动,而不掌握棋盘上的整体状态。

使用自然语言处理来比较病毒

原文:https://towardsdatascience.com/using-natural-language-processing-to-compare-viruses-6a281311d9a?source=collection_archive---------75-----------------------

可能加速病毒疫苗开发的研究

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

疾控中心Unsplash 拍摄的照片

N 编者按: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情电晕病毒的信息,可以点击 这里

2019 年 12 月,世界开始了解一种新病毒,俗称冠状病毒,科学上称为新冠肺炎。

当冠状病毒被宣布为全球疫情时,开发疫苗的竞赛就开始了。

假设:这篇文章提供了我对自己参与的一项研究的见解。提供的图片是我个人电脑的截图。我要感谢 Nikhil Malhotra 和 Akshit Kothari 给我这个机会参与这个研究项目。

本文旨在提供两种病毒与其蛋白质结构之间的简单可能的比较,并为加速病毒的分析提供可能的基础。

那么我是怎么做到的呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

弗兰基·查马基Unsplash 上拍摄的照片

数据是 AI 中最重要的实体!

但是病毒的数据大部分是一系列的化学化合物,这将超出我这个计算机专家的理解范围;).所以我花了一些我的互联网数据来建立一个病毒学的基础。

我的病毒学竞赛…

  1. 什么化合物导致病毒攻击人体?
  2. 它们形成什么类型的联系?(因为我知道一些…但不多)
  3. 病毒遗传对疫苗开发有影响吗?(我知道这似乎是显而易见的,但我想确定一下)
  4. 我能得到蛋白质结构的 3D 图吗?
  5. 我如何利用蛋白质结构来比较两种病毒?

所以,是的…这些是我的一些主要问题。我必须从某个地方开始,所以我从向量空间映射开始。

好的……那么什么是向量空间映射,我该如何使用它呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

埃文·丹尼斯Unsplash 上拍摄

简单来说,自然语言处理中的向量空间映射就是将文本处理成‘向量’(耶…字面意思)。更简单的说法是字嵌入。在单词嵌入中,通过给单词分配标记,将单词转换成数字。在我的例子中,我有像 C,O,N(碳,氧,氮)这样的原子,所以我标记了每个原子。

到了完成矢量化的部分…

我需要文本格式或任何可读格式的病毒蛋白质结构。蛋白质数据库(PDB)通过建立不同病毒的各种蛋白质结构的巨大数据库来解决这个问题。“pdb”格式。

[## RCSB·PDB

作为 wwPDB 的成员,RCSB PDB 根据公认的标准管理和注释 PDB 数据。RCSB PDB…

www.rcsb.org](https://www.rcsb.org/search)

这是. pdb 文件的样子…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我的电脑截图

  1. CA ‘-α碳,’ CB '-β碳
  2. 赖氨酸、ILE 等是氨基酸(一些化学术语)

我感兴趣的是三列中的浮点数。这些数字代表作为第一个肽链“1”一部分的相应原子的“x”、“y”、“z”坐标,依此类推。

为了获得这些坐标(如上所述),我需要一些能够读取每一列的包或任何阅读器。我偶然发现了 **Biopython,**一个处理 PDB 文件的 python 模块。

[## Biopython 教程和食谱

Biopython 项目是免费 python 开发者的国际协会(https://www.python.org…

biopython.org](http://biopython.org/DIST/docs/tutorial/Tutorial.html#htoc162)

我开始从他们的 PDB 文件中获取蛋白质结构中每个原子的坐标,并通过将数据添加到数据框中来绘制坐标图。我有这样的东西…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

“plotly”获得的麻疹病毒蛋白质结构截图

现在我们有了一个结构…一个 3D 结构,但是下一步呢?

现在最重要的部分——向量空间映射…

PDB 文件包含含有在 3D 空间中不同位置的原子的残基序列。我把每个原子组合成一个残数,然后把它转换成一个 NumPy 数组。

if(extension=='pdb'):
  data = parser.get_structure("pdb",main_virus_needed+'\\'+filename)
  initial_models = data.get_models()
  models = list(initial_models)
  for model in models:
    chains = list(models[0].get_chains())

    for chain in chains:
      residues = list(chains[0].get_residues())

      for residue in residues:
       atoms = list(residue.get_atoms())

       for atom in atoms:
         store_residue(atom)

store_residue()是一个函数,定义为通过以下方式获得矢量形式的原子坐标

<Atom object>.get_vector()

[## 生物。PDB,亚当,亚当

object — + | Atom 定义 Atom 类。atom 对象存储 Atom 名称(有和没有空格)、坐标、B…

biopython.org](https://biopython.org/DIST/docs/api/Bio.PDB.Atom.Atom-class.html#get_vector)

看起来我已经准备好了,但是我考虑了一个问题,如果一些蛋白质结构比另一些更小或更大,那么需要多一个步骤来解决这个问题……填充!

所以我决定添加零填充并匹配向量的形状,并使用np.ravel()得到一个连续的扁平 1D 数字数组。

为了**比较,**我决定用余弦相似度…

为什么余弦相似?

作为一个有计算机背景的人,我没有任何病毒学背景,也不知道病毒学家用来比较两种病毒的方法。所以为了建立基础,我用余弦相似度。我知道这不是最理想的方法,但它让我可以根据一些计算得出一些不准确的结论。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

余弦相似度的基本公式

[## 余弦相似性

余弦相似性是内积空间的两个非零向量之间的相似性的度量。它被定义为…

en.wikipedia.org](https://en.wikipedia.org/wiki/Cosine_similarity)

一些结果…

以下是使用 Matplotlib… 与冠状病毒的蛋白质结构比较的不同病毒 I 的一些蛋白质结构的统计

  1. 麻疹
  2. 登革热
  3. 埃博拉病毒
  4. 丙型肝炎
  5. 艾滋病病毒
  6. 甲型流感
  7. 中东呼吸综合征
  8. 肺炎
  9. 严重急性呼吸综合征
  10. 轮状病毒

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Matplotlib 绘制

通过观察上面的统计,我对 SARS 和新冠肺炎的比较结果惊讶为 57.95%,因为他们来自同一个家族,相似度应该很高。但是伊波拉和轮状病毒也像新冠肺炎一样引起呼吸困难、咳嗽、胸痛等呼吸系统疾病,分别有 91.03%和84.71%的相似链。因此,只比较蛋白质链可能是加速疫苗开发的起点。

D 是声明者**:结果需要实验室和从业者的验证**。

下面是解决问题的不同方法的链接—

[## 新冠肺炎蛋白质比较分析

计算分析:微生物的世界我们生活在一个世界,这个世界在一月份受到了一场…

www.linkedin.com](https://www.linkedin.com/pulse/covid-19-protein-comparison-analysis-nikhil-malhotra/)

“开头是作品最重要的部分!”

谢谢你通读这篇文章,我将非常乐意回应你的反馈

在数据块上使用 Neo4j 和 PySpark

原文:https://towardsdatascience.com/using-neo4j-with-pyspark-on-databricks-eb3d127f2245?source=collection_archive---------16-----------------------

理解大数据

释放 Spark 和图形数据库携手工作的全部潜力

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fernand De CanneUnsplash 上拍摄的照片

随着最近发布的利用 Spark 数据源 API 的 Apache Spark 官方 Neo4j 连接器,在 Apache Spark 环境中查询 Neo4j 数据的方式发生了根本性的变化。除了这一变化,以前的 Neo4j Spark 连接器被标记为不推荐使用。在本文中,我想分享一个更新的端到端工作流,该工作流设置 Neo4j 和 Spark 的完全互连配对,该配对利用了新连接器的功能。

在这个过程中,我们将首先使用 Azure 虚拟机设置一个 Neo4j 云实例。之后,我们将设置一个运行 Spark 的 Azure Databricks 实例,然后使用 Apache Spark 的新 Neo4j 连接器在两个资源之间建立连接。如果您已经有了一个运行良好的 Neo4j 或 Databricks 实例,那么您当然可以跳过相应的步骤。但是,请注意每个步骤顶部的兼容性信息。

步骤 1:设置 Neo4j 云实例

首先,简单说一下兼容性:我们将使用的连接器支持 Neo4j 及更高版本。不支持 3.5 之前的版本。但是,它支持 Neo4j 企业和社区以及单实例虚拟机、因果集群和 Neo4j Aura。本文将关注单实例虚拟机的工作流。

作为我们的 Neo4j 实例,我们将使用正式的 Neo4j 企业虚拟机映像。最新版本在 Azure marketplace 中被列为 Neo4j Enterprise VM 版本 4.1 。如果你没有企业许可证,有 Websoft9 提供的社区版本的图片以及 Bitnami 提供的容器图片:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Azure marketplace 上 N eo4j 的搜索结果(作者截图)

虚拟机部署后,导航到它的 Networking 选项卡,确保它的端口设置是正确的:为了从 Spark 查询它,一个 螺栓端口必须允许入站流量。默认情况下,这是端口 7687。此外,我们将使用 Neo4j Web 接口来填充数据库,为此我们需要一个开放的 HTTPHTTPS 端口。默认情况下,它们映射到端口号 7474 和 7473。如果缺少这些端口规则中的任何一个,点击添加入站端口规则按钮添加它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Neo4j 实例的入站端口设置(作者截图)

用数据填充 Neo4j 实例

验证端口设置后,从其概述选项卡中获取虚拟机的公共 IP,并通过导航到 http://YOUR。VM.IP.ADDRESS:7474 。对于新启动的虚拟机,Neo4j 可能需要几分钟才能启动并接受入站连接。如果您使用 VPN 或代理,请确保对它们进行了相应的配置,否则您可能会收到“此站点无法访问”错误。

如果所有设置都正确,将弹出 Neo4j 浏览器 Web UI:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Neo4j Web UI(浏览器)(作者截图)

系统将提示您输入登录凭据。输入登录数据库的默认用户名和密码:

  • 用户名: neo4j
  • 密码: neo4j

第一次访问时,出于安全原因,您必须立即更改默认密码。选择一个可靠的密码并登录后,就可以填充数据库了。在本指南中,我们将使用 Cypher 查询语言通过在命令行中键入以下内容来创建一个简单的虚拟数据集:

UNWIND range(1,100) as id
CREATE (p:Person {id:id}) WITH collect(p) as people
UNWIND people as p1
UNWIND range(1,10) as friend
WITH p1, people[(p1.id + friend) % size(people)] as p2
CREATE (p1)-[:KNOWS {years: abs(p2.id - p2.id)}]->(p2)

因此,将创建一个类似于下面的数据结构,其中每个 Person 节点都用一个 ID 标记,每个 KNOWS 关系都有一个附加的(任意的)属性 years ,它描述了双方认识多久了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果数据结构,限于 5 个节点(作者截图)

就是这样!我们的 Neo4j 图形数据库已经启动并运行,迫不及待地想从 Spark 中查询。我们现在将继续设置一个合适的 Spark 环境。

步骤 2: 配置火花环境

再次,关于兼容性的重要说明:在撰写本文时, Neo4j 不支持 Spark 3.0 的连接器。因此,我们将不得不退回到 Spark 2.4 环境,以便与 Neo4j 通信。

对于我们的设置,我们将使用 Azure Databricks 实例。在 Azure marketplace 上搜索数据块并创建一个新资源。除了通常的设置(资源组、名称、位置和定价层),不需要任何特殊的配置设置。

部署完成后,通过导航到 Azure 资源的概述选项卡并单击启动工作区按钮,打开 Databricks 工作区。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(作者截图)

首先,我们需要一个集群。导航到集群选项卡并单击创建集群按钮以显示集群配置工具:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Databricks 集群创建屏幕(作者截图)

选择集群名称和可用性模式,并根据您的喜好配置工作节点的大小和数量。现在,请记住,我们被迫使用 Spark 2 设置——幸运的是,Databricks 仍然提供各种 Spark 2.4.5 发行版。确保在 Databricks 运行时版本字段中选择其中之一,例如运行时版本 6.6 运行 Spark 2.4.5Scala 2.11

启动您的集群,一切就绪!

步骤 3: 在 Neo4j 和 Spark 之间建立连接

既然我们现在已经启动并运行了 Neo4j 和 Databricks,那么是时候关注它们之间的联系了。为此,我们需要将 Apache Spark 的 Neo4j 连接器添加到我们的 Databricks 集群中。

在数据块中,导航到集群选项卡。选择先前创建的集群并访问其库选项:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Databricks 集群视图(作者截图)

现在,通过点击安装新的按钮,选择 Maven 并点击搜索包,为 Apache Spark 添加 Neo4j 连接器。键入“neo4j”查看所有可用选项。在撰写本文时,搜索中出现了三个包:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(作者截图)

  • neo4j-spark-connector :这是弃用的连接器版本(2.4) 。它不再受到开发者的积极支持。
  • neo4j-connector-Apache-spark _ 2.11:这是当前的连接器版本(4.0),也是我们将要使用的。
  • neo4j-connector-Apache-spark _ 2.12:这也是当前的连接器版本,唯一的区别是它是为 Scala 版本 2.12 编写的。因为我们的 Databricks 环境运行 Scala 2.11,所以这个不适合我们的目的。

根据开发人员的说法,由于 API 的不同,将两个连接器分开是必要的:

由于 API 的差异,不同的 scala 版本需要不同的 JAR 文件。确保您有适合您的环境的 JAR 文件。

阅读 Neo4j

连接器安装完成后,在工作区选项卡中创建一个新的 Jupyter 笔记本(右击→ 新建)。在插入 Neo4j 虚拟机的 IP、用户名密码后,通过运行以下命令尝试读取 Neo4j 数据库的节点数据:

df = spark.read.format("org.neo4j.spark.DataSource")\
 .option("url", "bolt://***XXX.XXX.XXX.XXX***:7687")\
 .option("authentication.type", "basic")\
 .option("authentication.basic.username", "***neo4j"***)\
 .option("authentication.basic.password", "***password"***)\
 .option("labels", "Person")\
 .load()
display(df)

如果您收到无法连接错误,请确保您的 Neo4j 虚拟机仍在运行,并且其 Bolt 端口接受入站流量。如果查询返回

java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V

您很可能为错误的 Scala 版本安装了连接器——确保您选择的是 Scala 2.11 版本(见上文)。

如果一切都解决了,查询将返回如下所示的数据帧:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

演示查询以数据帧格式返回的结果(作者截图)

作为 Python 的替代,您也可以通过在笔记本单元格顶部添加 %scala 魔法命令来使用 Scala 进行查询:

%scalaimport org.apache.spark.sql.{SaveMode, SparkSession}val spark = SparkSession.builder().getOrCreate()val df = spark.read.format("org.neo4j.spark.DataSource")
 .option("url", "bolt://***XXX.XXX.XXX.XXX***:7687")
 .option("authentication.type", "basic")
 .option("authentication.basic.username", "***neo4j"***)
 .option("authentication.basic.password", "***password"***)
 .option("labels", "Person")
 .load()display(df)

写入 Neo4j

写入图形数据库的工作方式类似。在下面的查询中,我们将更新原始数据,以包括前两个节点的名称:

df = spark.createDataFrame(
 [(1, "John"),(2, "Thomas")],
 ["id", "name"]
)df.write.format("org.neo4j.spark.DataSource")\
 .option("url”, "bolt://***XXX.XXX.XXX.XXX***:7687")\
 .option("authentication.type", "basic")\
 .option("authentication.basic.username", "***neo4j***")\
 .option("authentication.basic.password", "***password***")\
 .option("labels", ":Person")\
 .option("node.keys", "id")\
 .mode("Overwrite")\
 .save()

请注意,我们使用了*“Overwrite”*模式,并将 node.keys 选项设置为 DataFrame 的 id 列,以便将新值附加到现有节点。事实上,再次运行 read 查询将返回我们之前的结果,该结果用前两个节点的 name 属性进行了更新:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

写入数据库后读取查询的结果(作者截图)

就是这样!🎉您已经成功地建立了一对完全基于云的互联 Apache Spark 和 Neo4j,使您能够充分利用“传统”大数据和图形数据库的潜力。

要获得更多的查询示例和语法概述,请深入阅读官方的 Neo4j 连接器 for Apache Spark 文档或查看快速指南阅读编写来自/到 Neo4j 的。其他示例可以在 Zeppelin 笔记本示例库中找到。连接器还支持 Cypher 查询,允许您重用 Neo4j 桌面/ Web 应用程序中的现有查询。密码查询示例可在官方密码页面上找到。

利用网络科学探索 Instagram 上的标签文化

原文:https://towardsdatascience.com/using-network-science-to-explore-hashtag-culture-on-instagram-1f7917078e0?source=collection_archive---------12-----------------------

如何从 Instagram 中抓取标签,并使用 NetworkX 建立标签之间的关系模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:https://www . digital ambience . com/insta gram-light-forest-by-hey-hush/

摘要

该项目提出,网络科学和图论可以用来有效地分析 Instagram 上的标签文化。Instagram 标签的关系特征可以通过这些技术实现,这允许更深入地理解跨标签的对话和主题。

方法

为了演示这个想法,我写了一个实用的技术演练,演示如何使用 Python 和 NetworkX 来实现这一点。

大约有 37000 条 Instagram 帖子至少提到了一次#happiness,这些帖子都是从 Instagram 上删除的。语料库中所有唯一的标签(除了# happy)代表图中的节点如果标签在同一篇文章中被提到,它们之间就会形成边缘

这产生了一个无方向的“幸福”图,然后使用社区检测和各种中心性测量进行分析。

调查的结果

社区检测确定了三个集群,解释为:

  • 快乐的方面似乎是关于人们所做的事情和经历(例如:’#摄影#夏天,**#旅行#家庭)
  • 幸福的方方面面似乎大体上是关于人们如何思考和感受的(例如:生活、动机、T21、灵感、名言)
  • 第三个非常独特的集群完全是关于*#婚礼、#庆典#派对*

结论和应用

这种方法表明,图论可以成为探索社交媒体元数据(如标签)的一种非常直观和有用的方法。它可以应用于两个直接领域:

1.我们可能希望绘制主题实体的图形,例如,发布可持续发展、英国退出欧盟、冠状病毒的人

2.我们可以使用图表来模拟和理解围绕特定品牌、事件和地点的对话

(我们需要记住 Instagram 上内容的性质,因为这将指导什么是合适的应用)

可以在我的 GitHub 上找到复制该任务的所有功能代码:

[## kitsamho/insta gram _ Scraper _ Graph

这里有两个 Python 文件,每个文件都包含一个自定义的 Instagram 类。您需要有一个 Instagram 帐户…

github.com](https://github.com/kitsamho/Instagram_Scraper_Graph)

内容

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乔安娜·科辛斯卡在 Unsplash 上的照片

1)背景和背景

  • Instagram 是巨大的
  • 标签文化:让数据科学家的生活更轻松!
  • 一张图片胜过千言万语…还是一堆标签?
  • Instagram 上的‘快乐’对人意味着什么?

2)网络科学和图论

  • 什么是图?
  • 我们可以在分析中使用图论的哪些方面?
  • 关于图表的进一步阅读

3)在 Python 中构建功能

  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉独特的 Instagram URL
  • 使用多线程处理从 html 解析标签和其他内容
  • 特征生成和探索性分析
  • 数据选择
  • 图形构建
  • 可视化/分析和解释

1)背景和背景

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 NoisepornUnsplash 上拍摄

Instagram 是巨大的

Instagram 成立于 2006 年,是一个创意表达的小众场所。早期用户蜂拥而至,热衷于分享他们的城市涂鸦、豆奶拿铁和拉面的照片。iPhone 1 + Instagram 是酷的标志——数码潮人的黄金时代。

唉,这并没有持续多久。Instagram 的中产阶级化是不可避免的,当脸书在 2012 年收购 Instagram 时,他们迅速通过广告将其货币化,并将 Instagram 变成了一棵巨大的摇钱树。

2020 年,Instagram 不再酷,它是一个商业巨兽。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:https://www . statista . com/statistics/253577/number-of-monthly-active-insta gram-users/

据估计 有 10 亿活跃月用户5 亿 每日故事,Instagram 的崛起和影响力令人印象深刻。

随后,Instagram 在许多方面已经成为主流消费文化的可靠晴雨表,这意味着它本身是一个潜在的有用工具,可以用来了解人们的情况。

虽然有理由假设 Instagram 的图片和视频将是主要的“首选”数据来源,但我发现分析 Instagram 的标签文化是一种更有效的方式来了解 Instagram 上出现的主题和话题。再加上从网络科学中借用的分析技术,意味着主题和话题之间的关系可以更容易理解。

标签文化:让数据科学家的生活更轻松!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由布丽娜·布鲁姆Unsplash 上拍摄

数据科学家工作的一个更重要的部分是能够投入时间来确保我们的数据符合目的,这包括彻底清理我们的数据。这可能会占用大量时间,尤其是社交媒体数据往往非常非结构化并且经常夹杂着“噪音”。

如果没有足够的预处理,我们对社交媒体数据使用的自然语言处理技术可能会导致难以解释和洞察的输出。

我们可以使用各种方法来清理和组织我们在社交媒体上发现的原始语言,但当用户为我们做这件事时,它变得更加容易。Instagram 上的标签文化——本质上是有组织的、正常的和不那么喧闹的——是一种文化现象,为我们做了所有这些艰苦的工作。

一张图片胜过千言万语。还是一堆标签?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乔治·帕甘三世在 Unsplash 上的照片

由 octothorpe 符号(#)引入的标签是一种元数据标签,用于 Twitter 和其他微博服务等社交网络。它允许用户应用用户生成的动态标签,帮助其他用户轻松找到具有特定主题或内容的邮件"

人们经常用一系列标签来结束 Instagram 帖子。标签可以提供那个人当时在想什么、感觉什么、观察什么的简明摘要。标签提供了图片本身无法提供的上下文和含义。

让我们找一个例子来说明这一点。

Instagram 上的‘快乐’对人意味着什么?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

蒂姆·莫斯霍尔德在 Unsplash 上的照片

社交媒体越来越有可能被抨击为让人痛苦的东西。可悲的是,这是事实。

让我们逆势而为,翻转这个真相,在 Instagram 上探索的快乐

以下 Instagram 帖子在帖子正文中包含了**#幸福**。从这篇文章中,我们可以单从图片中解读出什么?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://www.instagram.com/thatdesivegangal/—包含#幸福的公众号

不多。也许和音乐有关?

除了显而易见的以外,从这张照片上几乎看不出什么。当他们发布这个的时候,他们还在想什么/感觉什么?

现在,当我们查看这个人在他们的帖子旁边制作的所有标签时,我们对这个人以及他们在那个时刻与**#快乐**的联系有了更多的了解。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://www.instagram.com/thatdesivegangal/—包含#幸福的公众号

对这个人来说,快乐与积极、动力、改变和减肥联系在一起。

虽然 Instagram 图片能告诉我们一些东西,但人们使用的标签能告诉我们更多的东西。

扩大标签分析

现在,这只是一个 Instagram 帖子,包含了**#幸福**。当我们看到周围其他人的帖子的标签提到**#幸福时会发生什么?**

如果我们扩大分析范围,查看成千上万条包含快乐的帖子,会发生什么?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Instagram 上所有包含# happy 的帖子

很快我们就会有大量的标签需要导航。什么样的可扩展方法可以让我们理解所有这些数据?

这就是我们可以使用网络科学和图论来帮助我们的地方。

2)网络科学和图论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由安娜斯塔西娅·杜尔吉尔Unsplash 上拍摄

网络科学是一个蓬勃发展且日益重要的跨学科领域,其重点是将复杂的社会、生物和技术系统表示、分析和建模为网络或图形

来源:http://tuvalu.santafe.edu/~aaronc/courses/5352/

我喜欢把网络科学和图论看作是让我们理解事物如何联系的方法。我们可以借用网络科学和图论的一些基本原理来理解insta gram 上的 hashtags 是如何连接的。

与网络科学和图论相关的主题非常密集,需要很长时间才能涵盖。这将分散项目的实际重点。然而,有一些简单但基本的概念需要解释。

什么是图?

一个 (G) 是一个网络的抽象表示。图是由顶点 (V)边(E) 其中***【𝐺=(𝑉,𝐸】***

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:https://www.computerhope.com/jargon/n/node.htm

  • 图中的节点 (V) 代表我们数据中存在的唯一数据点。在 hashtag 分析的情况下,hash tag 表示我们网络中的节点是有意义的。因此,如果我们的 #happiness 数据集只有两个标签,即 #A#B ,我们将有两个节点

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代表两个 hashtags 的两个节点

  • (E) 是表示节点间某种关系的连接。在分析标签的情况下,将这种关系表示为某种事件可能是有意义的,即,如果标签 #A 在与# B 相同的帖子中被提及,我们将假设在 #A#B 之间存在关系,因此我们将在这两个节点之间创建一条边

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果两个标签在同一篇文章中被同时提及,那么它们之间就会形成一条边

当我们将其他帖子的标签添加到图表中,并模拟它们与之前所有帖子的关系时,我们开始看到结构形式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随着更多的标签被添加到图表中,我们可以看到哪里有联系,我们可以开始看到结构形式

我们可以在分析中使用图论的哪些方面?

  • 社区检测。我们可以使用算法来识别和标记与**#快乐相关的主题群。**在这个例子中,我们有 14 个标签,它们都以不同的方式连接在一起,但是形成了不同的簇。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

识别图中的标签社区

  • 度中心性/中介中心性。我们可以计算出网络中哪些标签在连接整个网络时特别重要。就像希思罗机场连接了很多世界一样,什么标签连接了#幸福**“风景”?**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

确定哪些标签在图表中起着核心作用

  • 可视化。如果我们用散点图来绘制网络,这是一种非常引人注目的方式来可视化大量关于幸福的信息

关于图表的进一步阅读

迈尔·杨奇煜 写了一整个系列,很好地涵盖了图论的内容。如果你觉得这个话题有趣,我强烈建议你在某个时候读一读这篇文章。

[## 图表介绍(第一部分)

Python 中的主要概念、属性和应用

towardsdatascience.com](/introduction-to-graphs-part-1-2de6cda8c5a5)

现在,让我们看看构建网络需要采取的实际步骤。

3)在 Python 中构建功能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

凯文·Ku 在 Unsplash 上拍摄的照片

我构建了两个 Python 类,处理从捕捉数据到构建、可视化和分析幸福图的所有过程。每个类别中的关键管道概述如下。

**class InstagramScraper()**
  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉唯一的 Instagram URL 的
  • 使用多线程处理从 post html 解析标签和其他内容
**class InstagramGraph()**
  • 特征生成&探索性分析
  • 数据选择
  • 图形构建
  • 可视化

您可以在这里访问这些类的完整代码及其相关的自述文档:

https://github.com/kitsamho/Instagram_Scraper_Graph

InstagramScraper 类()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:https://hacker noon . com/how-to-scrape-a-website-not-get-black-list-271 a605 a0d 94

**class InstagramScraper()**
  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉独特的 Instagram 网址
  • 使用多线程处理从 html 解析标签和其他内容

这一阶段结合使用自动网页浏览和 html 解析从 Instagram 帖子中抓取内容。我构建了一个定制的 Python 类 InstagramScraper t ,它包含了一系列抓取和数据提取方法,可以返回抓取的 Instagram 数据的 Pandas DataFrame。

我将用几个示例代码块向您展示下面的主要过程,以突出所涉及的主要步骤。

使用 Selenium WebDriver 在 Instagram 上搜索标签

Instagram 上有一个功能,用户可以搜索特定的标签,Instagram 会返回包含该标签的所有帖子。这是我们获取所需数据的第一步;使用 Selenium webdriver 让 Instagram 为我们完成这项工作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Instagram 上搜索包含# happy 标签的 Instagram 帖子

捕捉独特的 Instagram URL

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Selenium WebDriver 捕获独特的 Instagram URLs

Instagram 上的每个帖子都有自己的唯一网址。在我们搜索了所有包含特定标签的文章之后,第一步是获取所有包含该标签的文章的唯一 URL。

我发现最好的方法是结合使用 Selenium WebDriver 和动态解析 html,当我们滚动浏览所有内容时,提取每个 posts href 属性到一个数据结构。

由于 Instagram 网站的这一部分是高度动态的,需要一致的滚动来加载新图像,使用某种基于浏览器的自动化,如 Selenium WebDriver,是捕捉数据的唯一可行的方法。这意味着获取所有链接可能需要一点时间。

捕捉唯一的 Instagram URL 示例代码

使用 Selenium web driver & Beautiful Soup 捕捉独特的 Instagram URL。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

解析标签和其他内容

一旦你有了所有链接的列表,你就可以结合使用 urlopen 和 html 解析(我用的是 Beautiful Soup )来获取每个 Instagram 帖子的数据。

一旦你有了每个帖子的 html 并解析了它的内容,你会发现 Instagram 帖子的数据有一个类似字典/ JSON 风格格式的一致结构

解析标签和其他内容—示例代码

每个 Instagram 帖子都包含 dictionary / JSON 类型结构的数据。我们使用单独的函数迭代每个帖子,提取我们想要的数据。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

InstagramScraper 输出

InstagramScraper 类的主要输出是一个 Pandas 数据帧,其中包含大量 post 变量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 InstagramScraper 类的熊猫数据帧输出

这些变量中的大部分都是自解释的,并且提供了各种各样的选项来进行 hashtags 之外的探索性分析。

现在我们有了收集的数据和一个漂亮整洁的熊猫数据框架,我们可以开始考虑处理我们的数据,为建立我们的网络和可视化我们的图表做准备,这是事情变得真正有趣的地方。

InstagramGraph()类

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:https://TechCrunch . com/WP-content/uploads/2017/12/insta gram-hash tags . png?w=730 & crop=1

我构建了第二个 python 类,名为 InstagramGraph ,它也包含一系列方法,允许您分析 Instagram 数据集,并最终使用 Plotly 可视化将 hashtag 数据建模为 NetworkX graph 对象的实例。

**class InstagramGraph()**

该类别中的关键流程包括:

  • 特征生成&探索性分析
  • 数据选择
  • 图形构建
  • 可视化/分析&解读

特征创建&探索性分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

瓦迪姆·谢尔巴科夫Unsplash 上拍摄的照片

下面的代码块概述了来自 InstagramGraph 类的管道,该类包含一套在我们的数据中创建新特征的方法。

一系列方法在我们收集的 Instagram 数据中生成新特征。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

这里有几种方法,一些是语言学的(例如确定帖子的语言),尽管这种方法中的大多数过程产生额外的描述性指标,例如按用户的平均帖子计数、按帖子的总标签计数和按用户的平均标签使用。

虽然这些新特性/指标中的一些与图形构建没有直接关系,但总是值得生成更多的数据点,因为它提供了对主题进行一些探索性分析的机会,这可能会提供更多关于我们的图形模型所揭示的内容的上下文。它也可能激发一些你最初没有考虑过的其他分析想法。

让我们来探索其中的一些。

语言

Python 中的 langdetect 库允许我们通过 API 访问谷歌翻译库,并赋予我们在文本中识别语言的能力。

注意——由于谷歌云 API 请求的限制,在处理大量数据时,运行速度会非常慢。出于举例的目的,这很好;如果你想生产这个,你可能会想一个更快的选择,并考虑支付。

包含# happy 的帖子的语言分布

正如我们所看到的,Instagram 上大多数关于 #happiness 的帖子都是英文的——超过我们数据集的 80%。

用户发布频率

对一些分布的观察表明,关于用户发布频率的数据是非常正偏的;有少数异常帐户发布非常频繁(多达 140 次),而大多数发帖者只发布一次。

在我们的数据中,用户发帖的频率——正常化为 1

提取标签

为了提取我们的标签,我创建了两个方法来完成这项工作。一种方法接受一个字符串输入并返回一个 hashtags 列表。如果人们没有在他们的标签串之间加入空格,代码会考虑到这一点,并将它们“解包”到单独的标签中。

从字符串中提取标签。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

分析

对帖子的标签频率的分析表明,虽然帖子的频率倾向于遵循均匀分布,但是有一些异常的帖子包含异常高数量的标签。进一步的研究表明,这部分似乎是由在数据集上多次发帖的人驱动的(高频发帖者)。所以从广义上来说,那些经常发帖的人也倾向于在他们的帖子中使用更多的标签。

虽然标签使用的分布基本一致,但高频发帖者在帖子中使用更多的标签

词条匹配标签

然后,第二种方法获取这个 hashtags 列表,并在适当的时候查看输入的 lemmatise 。对于那些不熟悉词条满足的人来说,这是我们将一个单词简化为其词根形式的过程。例如,如果我们有标签**#最快乐的#更快乐的**——这些都是动词**#快乐的变形。**因此,lemmatisation 将这些变化还原为 #happy。我已经使用了一个空间模型的实例来执行引理满足。

lemmating hash tags—示例代码

使用空间匹配标签。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

数据选择

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

维多利亚诺·伊斯基耶多Unsplash 上拍摄的照片

好了,下一步是选择我们想要建模的数据。代码概述了来自 InstagramGraph 类的管道,该类包含一套允许我们选择我们想要的数据的方法。

在我们构建图表之前,选择数据的子集。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

选择语言

虽然 Instagram 是一个国际平台,但为了简单起见,我们可能会考虑过滤掉非英语帖子。正如我们之前看到的,超过 80%的关于**#幸福**的数据是英文的,所以关注这些数据的子集是有意义的。

删除已验证的用户

经过验证的用户往往是品牌、名人或网上商店,即不代表 Instagram 普通用户的“人”。根据您的分析目标,您可能需要考虑从数据中删除已验证的用户。

移除高频海报

对高频海报的粗略检查显示,这些有时是未经验证的账户,在未经验证的状态下使用 Instagram 出售东西。我们觉得这代表了我们想了解的观众吗?如果是,我们可以把它们留在里面,如果不是,我们可以在构建图表之前把它们过滤掉。

使用 lemmatised 标签?

我认为在可能的情况下选择 lemmatise 抓取的标签是有意义的,因为它减少了构建图表所需的数据量——我们不一定需要所有额外的数据。

图形构建

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

兰迪·法特在 Unsplash 上的照片

既然我们已经选择了要建模的数据,我们就可以构建图表了。我们为此使用的管道如下:

InstagramGraph 类中的方法,包含允许创建包含 hashtag 数据集的边和节点数据的 Graph 对象的进程管道。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

这条管道中有三个主要流程:

编译一个列表列表

这只是将 hashtags 从我们的数据帧提取到一个列表列表中——每个子列表代表我们的 #Happiness 数据集中的一篇文章。通过将输入作为列表的简单列表,我们可以很容易地将这种图形构建方法重新用于将来的其他数据源,在那里我们可以以类似的结构/列表格式的列表来争论原始数据

编译列表列表—示例代码

编辑一个由 n 个列表组成的列表,其中每个列表包含一个人的标签,n 是 Instagram 帖子的总数。默认的停用词被删除,并可选择添加更多停用词。资料来源:https://github.com/kitsamho/Instagram_Scraper_Graph

编译列表列表—示例输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从一系列标签中提取一个列表列表

识别边和节点

下一步是获取我们的列表列表,并使用它来生成存在于整个数据集的节点和边。请记住,节点是数据集中唯一的标签,如果任意两个标签在同一篇文章中同时出现,就会创建一条边。

识别边和节点—示例代码

计算 hashtag 数据中存在的所有节点和边——保留几个数据帧作为属性。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

识别边和节点-示例输出

该方法将两个生成的数据帧保存为属性—一个包含所有节点的数据帧和一个包含所有边的数据帧。

节点数据框架

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

节点数据帧——每一行代表#Happiness 数据集中存在的一个唯一的标签

边缘数据帧

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

边缘数据框-每行代表#Happiness 数据集中存在的唯一边缘。边缘频率是指该配对在#Happiness 数据集中出现的次数

构建图表

下一步是将这些节点和边添加到 NetworkX 图形对象(G)的实例中。

构建图表—示例代码

获取边和节点数据并将其添加到 NetworkX graph 对象的实例中。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

构建图表—示例输出

一旦我们创建了 NetworkX 图形对象,我们就可以使用各种计算出的图形属性来进一步增强我们的节点数据。

  • 中间中心性
  • 邻接频率
  • 聚类系数
  • 社区

用图形属性增强的节点数据框架

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用来自 NetworkX 的新指标增强了节点数据框架

可视化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 russn_fckrUnsplash 上拍摄

现在我们有了一个 NetworkX graph 对象的实例,它是用从我们的 #Happiness 数据集中提取的节点和边数据构建的。

曲线图

我们现在可以使用 Plotly 将 hashtag 图可视化为一个相连的散点图。

在 Plotly 中将 hashtag 图绘制成散点图,带有各种参数,允许定制显示的数据。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

我提出了几个论点,允许对视觉化进行一定程度的定制。

**def plotGraph**(node_size=’adjacency_frequency’,colorscale=’Viridis’)

我认为使用节点大小来传达一些其他变量是有意义的——默认为“邻接频率”,即一个节点与多少个其他节点有边连接。在这种情况下,较小的节点将代表具有较少边的 hashtag,相反,较大的节点将具有较多边。我们可以通过应用与节点大小相关的色标来进一步强调这一点。

**def plotGraph**(layout=nx.kamada_kawai_layout)

NetworkX 有一些不同的图形布局。kamada kawai 布局以尽可能少的重叠边的方式排列节点。因此,这有一种趋势,节点的排列方式传达集群…但最重要的是,往往是最容易解释的布局。还有其他选择,如圆形布局、随机布局,但根据我的经验,这些更难理解。

**def plotGraph**(light_theme=True)

有两种颜色主题,浅色和深色。

调用绘图函数…

#幸福图的网络图可视化——节点的大小与它们到其他节点的边的频率正相关。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

我们可以很容易地看到在**#幸福**图中有几个连接良好的标签(有更多节点连接的标签)。微笑生活家庭动机、聚会……这些都是网络上经常被提及的标签。

正如我们所看到的,force directed 布局倾向于将连接良好的标签组推到一起。如果我们再次绘制图表,但使用社区标签对节点进行着色,我们可以更好地可视化社区是如何脱离的。

**def plotGraph(**community_plot=True)

#幸福图的网络图可视化——节点的颜色代表使用 NetworkX 计算的社区,节点大小代表邻接频率。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

旭日图

在**#幸福**网络中似乎有三个容易识别的社区。我们可以使用 sunburst 可视化来探索哪些特定的标签有助于每个社区。单击滚轮与集群互动。

在#幸福图中检测到的社区的交互式旭日可视化。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

社区检测确定了三个群集,解释为:

  • 快乐的方面似乎是关于人们做什么和经历什么(例如,#摄影#夏天,**#旅行#家庭)(片段 0)
  • 快乐的各个方面似乎大体上是关于人们如何思考和感受的(例如:生活、动机、灵感、引语)
  • 第三个非常独特的集群完全是关于*#婚礼、#庆典#派对*(第 3 部分)

结束语

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Artem Beliaikin 在 Unsplash 上拍摄的照片

我希望你喜欢这个关于如何从 Instagram 中提取标签数据,并使用从网络科学中借鉴的原理进行分析的实践练习。

我这个项目的主要目标是:

  1. 试图证明社交媒体数据中蕴藏着丰富的可访问性和潜在的深刻内容。
  2. 向你展示在你可能意想不到的地方几乎总是有有趣的模式

我选择了**#幸福**作为案例研究,因为希望这是我们都能涉及到的事情。然而,代码适用于任何标签。只要你能收集足够多的数据来建模,任何事情都有可观察到的关系。你可以考虑品牌、地点、事件甚至人。

你甚至不需要使用 Instagram 这些代码可以很容易地被重新利用,以使用 Instagram 之外的各种元数据。Twitter、LinkedIn 甚至抖音都有可能。

正如我之前提到的,代码在我的 GitHub 上可用,所以请随意用、进化改进。我很想看看人们用这个做什么。

下次见!🚀

山姆干杯

参考和链接

使用神经网络推荐网飞的节目

原文:https://towardsdatascience.com/using-neural-nets-to-recommend-shows-on-netflix-d4fbecffe0b0?source=collection_archive---------55-----------------------

机器学习应用

训练模型来预测上瘾的概率

没有什么比在网飞开始做一些事情,然后意识到 20 分钟内你不感兴趣更令人痛苦的了。在这个项目中,我试图通过计算在给定特定表演特征的情况下,我将完成一个表演的概率,将事情掌握在自己手中。

隔离 5 个月后,我非常确定我已经看完了我想在网飞看的所有东西。从积极的角度来看,能够在凌晨 2 点睡觉,9 点起床去工作对我的工作效率并不是很好。曾几何时,我真的很喜欢每个工作日步行 30 分钟到加州火车站,然后再回来。对我慢慢陷入彻底的无精打采的唯一安慰是,我一直在努力阅读网飞的原著。没什么可看的了,再加上决定让自己的时间变得更有效率,这两个因素促使我尝试使用神经网络来预测我接下来应该看什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 Unsplash自由股票的照片

步骤 1:获取数据

这个项目的第一步是获得我的观察活动。我喜欢网飞使这很容易访问,甚至下载为. csv 文件。

这是我在过去 3 年里看过的关于网飞的完整列表(只看超过 1 集的节目,我看过 100-200 个关于网飞的节目)。这让我有可能开发一个更加个性化的推荐系统。如果你是网飞的新用户,该平台通过使用与你相似的人的观看模式,避开了缺乏你观看活动数据的问题。

然后,我从 IMDb.com 搜集了一些数据,这些数据包含了很多电视剧的特征,比如类型、演员、创作者、剧集长度和标签。这里有一些快速汇总表,显示了我在网飞观看的节目中最受欢迎的流派、演员和标签。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

观看的热门类型(由 IMDb.com 定义的类型),按作者分类的图片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

热门观看标签(IMDb.com 定义的标签),作者图片

我决定不使用 actors,只是因为数据中可能有太多的变化来提供任何有用的信息——这最终会创建太多没有任何真正预测能力的特征(也称为过度拟合)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

顶级演员观看(演员在 IMDb.com 上搜索),图片由作者提供

有了一大堆用来预测我想看什么节目的特征(或自变量),接下来我需要创建一个因变量。因变量只是一个 1 或 0 的指示器,它描述了在我开始的情况下,我是否完成了一个节目。

步骤 2:预处理和特征选择

在这一步中,我们试图将我们的数据转换成一种可以用来运行神经网络的格式。我的数据集中的每个观察都是电视节目和季节的组合。比如《十三个为什么》第一季。我添加到数据集的特征包括作为分类变量的流派、标签和季节号,以及作为数值变量的剧集长度。

为了创建有用的东西,我必须将数据集转换成一个包含各种虚拟变量的大数据集。例如,对于“戏剧”类型,像“13 个原因”,“非正统”这样的节目以及该类型的其他 18 个节目的值为 1,而所有其他节目的值为 0。

因为我有大量的标签,所以我最终得到了一个包含 2000 多个特征的大数据集,但只有 200 多个观察值!这导致我不得不削减功能,以防止严重的过度拟合。为了做到这一点,我创建了一个相关性矩阵,查看所有特征之间的相关性。这使我能够删除那些不会提供任何新信息的高度相关的特征。例如,类别“犯罪”将与标签“谋杀”高度相关,因此“谋杀”将被移除。下面是实现这一点的一些示例代码:

X_corr = X.corr() columns = np.full((X_corr.shape[0],), True, dtype=bool) #Keep only columns with correlation less than 0.9 for i in range(X_corr.shape[0]): 
    for j in range(i+1, X_corr.shape[0]): 
        if X_corr.iloc[i,j] >= 0.9: 
            if columns[j]: columns[j] = False selected_X = X[X.columns[columns]]

使用上述相关矩阵作为特征选择方法的一个限制是,它不会拾取相关单词的相关性。例如,像“高中”和“高中生”这样的标签可能不会被上面的算法选中。如果有的话,这两者之间可能存在非常小的相关性(因此,在我项目的下一个实现中,我希望尝试一些单词关联算法来改进特征选择过程)。

第三步:型号选择

我使用 Keras API 运行 TensorFlow。有了我可以指定的一大堆参数,我决定尝试几个不同的参数,看看在将训练好的模型应用于测试集时,什么会产生最高的准确性。我决定关注以下杠杆:节点数、正则化系数和压差值。总之,这些杠杆可以提供一个最不可能欠拟合或过拟合的模型。

下面是一个代码示例,它创建了一个简单的参数矩阵,我希望执行网格搜索来确定最佳模型:

#Generate list of nodes to search over
nodes = np.arange(12, 61, 24)#Generate list of dropout values to search over
dp = np.arange(0.2, 0.81, 0.3)#Generate list of regularization coefficients to search over
reg = np.array([1e-4, 1e-5, 1e-6])params = list(product(nodes, dp, reg))

关于网格搜索的完整实现,请点击这里的链接!

以下是我在这个网格搜索过程中获得的一些经验:

  • **在开始前尝试几个型号;观察损失函数:**当我第一次开始运行几个测试模型时,我观察到我的测试集的损失函数在增加,这对于一个旨在最小化损失函数的模型来说是非常奇怪的。这个奇怪的现象是通过指定一个较小的步长来解决的,这样梯度下降就不会“跨过”最小值。下面是一些如何实现较小步长的示例代码:
from keras.callbacks import LearningRateScheduler#define when you want to reduce your step size by setting n
n = 50def lr_scheduler(epoch, lr):
    if (epoch > n):
        #define your step size here
        lr = 1e-5 
    return lr...model.fit(X_train, Y_train, epochs = 200, batch_size = 32,\                    callbacks = [LearningRateScheduler(lr_scheduler, verbose=1)])
  • 使用“提前停止”功能:这似乎是常识,但 Keras 中默认的“拟合”命令不会提前停止你的神经网络,即使下一次迭代没有在最小化你的损失函数方面提供显著的改善。此外,它不使用导致最低损失的权重,除非您使用keras.callbacks.EarlyStopping功能。下面是一个实现示例:
from keras import callbacksearlystopping = callbacks.EarlyStopping(monitor =”val_loss”,\ 
mode =”min”, patience = 30, restore_best_weights = True)

尽管你可能受到诱惑,但你不希望指定一个“val_accuracy”作为停止标准,因为这可能会导致过度拟合。

  • k 倍交叉验证的重要性:由于我的训练和测试集非常小,仅在一种类型的训练和测试集上尝试许多模型很可能会导致过度拟合。因此,对于每个模型,我在 10 个不同的训练测试集组合上进行尝试,并取平均准确度来选择一个模型。有许多包可以实现这一点,但是我实现了一个简单的 for 循环,并为训练测试分割的每次迭代使用了不同的种子。
for i in range (0, 10):     X_train, X_test, Y_train, Y_test = train_test_split(X, Y,\ test_size = 0.2, random_state = 1234 + i, stratify = Y)...#Fit your model here
  • 添加层的危险:一开始,我根据自己对神经网络功能的直觉,给我的模型添加了许多层。由于神经网络基本上试图创建新的特征,在给定现有特征的情况下最好地预测结果,所以我假设有更多的层会更好。然而,这导致了严重的过度拟合。因为我已经面临着适应太多特性的危险,所以创建一个更复杂的模型会加剧这个问题。
  • **并行处理的重要性:**当我第一次开始网格搜索来寻找参数的最佳组合时,我计算了一次运行的时间,然后意识到,对于我想要尝试的大约 729 个不同的模型,依次运行每个模型需要大约 14 个小时。使用multiprocessing包,我可以同时运行大约 12 个模型,将我的总处理时间减少到 2 个多小时。
#Always time your code first before running all your different models! Assume here that train_model is a function I created to train my model.start = time.time()
train_model(params)
end = time.time()
print(f"Runtime of the program is {end - start}")#Here is some simple code to perform parallel-processing for a grid searchimport multiprocessing as mp
pool = mp.Pool(processes = 12)
test = pool.map(train_model, params)

第四步:预测在网飞看什么

在想出一个模型来训练我的数据后,我导出了这组参数和权重,开始生成一个概率,即我将完成网飞上的每一场演出。

为了得到网飞所有的节目,我从 Reelgood.com 搜集了一些数据。事实证明,大约有 1700 个来自网飞所有版本(本地和外国)的电视节目。然后,我再次搜集了 IMDb.com 的数据,寻找所有这些节目的特征。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Reelgood.com的网飞演出的完整列表(作者截图)

最后,我将我的模型和权重应用到我的网飞头衔的完整列表中,我最终得到了我将获得每个网飞头衔的预测概率。

我的首选是一些我从未听说过,但听起来确实有点有趣的节目。以下是我的模型极力推荐的一些节目(括号中的概率指的是在我开始的情况下,我完成一个节目的概率):

  • 她一定要得到它(97.3%的可能性),根据斯派克·李的热门电影改编,讲述了一个年轻的黑人女性如何驾驭自己的爱情生活。听起来像是我喜欢的“亲爱的白人”!
  • 我通常不喜欢韩剧,但“之声”(96.1%的概率)——这是一个关于侦探解决谋杀案件的故事——听起来很有趣!
  • 《丽塔》(94.5%的可能性)——这是一个关于直言不讳、叛逆的老师的故事,她需要掌控自己的个人生活?这听起来有点像威廉姆·H·梅西在《无耻之徒》中扮演的角色,我非常崇拜他。此外,它来自丹麦,这使得它更加迷人!
  • 多莉·帕顿的心弦”(93.9%概率)——关于家庭的节目设置为多莉·帕顿音乐?听起来像是“这是我们”遇到了“小美国”,我就是为此而来的。

最后,我实际上看到了一些我最喜欢的节目与推荐节目的相似之处,这是一个很好的健全性检查,表明该模型是有效的。

总之,如果再来一次,我会做下面这些事情:

  • **模型中查看的构建日期:**人是会变的,所以模型需要跟上!我会喜欢 3 年前看的东西吗?从这个意义上来说,我最近看的电视节目的重量可能会增加,或者如果我厌倦了某个类型,重量可能会减少?不过,总的来说,我确实有轻微的强迫症,比如,只有去年感恩节去伦敦的时候,我才会看英剧。因此,在这种情况下,加权显示我刚刚完成的可能是有意义的。
  • **获取更多数据:**没有什么比更多数据更能帮助模型了。尤其是因为我总是处于过度适应我所拥有的功能的危险之中。也是看更多网飞的借口(尽管我刚刚订阅了 Apple TV+,相信我,当我告诉你你需要它的时候)。
  • **收集更多有见地的特征:**我是否忽略了其他比类型和标签更具描述性的特征?例如,烂番茄分数?(我认为这通常能很好地预测我的观看活动,但也有一些明显的例外,比如《13 个为什么》第三季——我对此没有借口,也不知道为什么我会如此上瘾)。
  • **执行文本关联:**如果我有一些 NLP 经验,我会使用比相关矩阵更复杂的算法来选择我的特征。但是,嘿,这是另一个项目的想法,不是吗?
  • **选择基于高召回率的模型:**我稍微想过这个问题,但是考虑到任务的性质,选择召回率更高的模型不是更好吗?虽然这可能会在推荐的节目中产生很多噪音,但有更多的假阳性几乎没有什么代价。

然而,这些都是以后的想法。今晚,请原谅我回去完成“13 个为什么”的原因,这样我才能继续我的生活。

在我的 Github 上可以看到这个项目的完整代码:

[## Bryan chaws/网飞 _ml_project

机器学习项目-使用神经网络来预测显示,我会喜欢基于过去的观看活动。01 …

github.com](https://github.com/bryanchiaws/netflix_ml_project)

使用神经网络改善废物管理,一次一个传播。

原文:https://towardsdatascience.com/using-neural-networks-to-improve-waste-management-one-propagation-at-a-time-b2b7414d329d?source=collection_archive---------38-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一。马累 Thilafushi 的废物处理场。哈特斯托克-穆罕默德阿卜杜勒拉希姆

问题范围

这绝对不好看,也不是在餐桌上提出来的东西,尽管每顿饭结束后,这个过程又开始了,我说的是浪费!

废物管理是收集和处理废物的过程,是每个社会不可分割的一部分,通常反映了一个社会的整体发展水平。废物管理的低端是一个非常遥远的过程,通常只涉及一个露天垃圾场。另一方面,这些过程涉及多阶段的努力,有针对性的资助,以便收集、分类和重新利用废物以备将来使用,或完全分解废物以产生能源。世界上存在的大多数废物管理系统都介于这两个极端之间。然而,不幸的是,上图中的 Thilafushi 城市垃圾填埋场更常见。

目前,世界每年产生 20 亿吨城市固体废物,预计到 2050 年将增加到 34 亿吨。高收入国家占世界人口的 16%,却产生了 34%的垃圾。因此,预计随着人口更多的发展中国家继续进步,产生的废物量也将增加。

这是个大问题。

幸运的是,我们有机会从错误中学习并改变!

数据如何产生影响

数据帮助我们量化和结构化问题。数据科学家使用他们所掌握的数据来提供见解,从而为解决此类问题的决策提供信息。一个典型的过程可以总结为 3 个一般步骤:

  1. 确定需求
  2. 测量问题
  3. 构建定制解决方案

就废物管理而言,这一过程极其复杂,需要在地方政府和社区的支持下采取多学科方法才能取得成功。在这篇文章的剩余部分,我将特别关注这个过程的一个方面,通过图像分类进行垃圾分类。

由于不同类型的废物需要特定的解决方案,确保废物得到适当分类对于建立有效和高效的废物管理系统至关重要。请看下面(图 2 )的示例,了解典型的分类设施可能是什么样子,以及废物是如何进行初步分类的。那么大的问题应该是显而易见的,我们如何用可以结合技术的系统来取代现状,以提高这一过程的有效性和效率?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2。左图:这只是垃圾分类设施众多可能例子中的一个。右图:最常见的分类方法是手工分类,这非常耗时,而且成本极高。来源

例如,某些塑料可以回收并再次用作生产原料,然而,一种塑料所需的再加工通常与另一种塑料大不相同。这些差异的细节超出了本文的范围,但是如果你有兴趣了解更多,请查看这个网站

那么,什么是图像分类,它是如何工作的?下面我简单解释一下。

利用神经网络进行垃圾分类

神经网络(也称为 多层感知器【MLP】)是一种预测建模技术。像许多其他建模技术一样,神经网络涉及通过迭代过程最小化成本函数。然而,与其他一些模型不同,神经网络的体系结构非常健壮,能够相对容易地处理回归和分类问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3。一种基本的神经网络架构,也称为多层感知器。资料来源:走向数据

神经网络由多层节点或神经元组成,因此得名(见图 3 )。第一个是输入层,它接收预处理的数字数据。该层中的节点数量取决于输入数据本身。最后一层被称为输出层。这一层的节点数量完全取决于问题的性质。例如,回归(或二元分类)问题将涉及一个输出节点,而多类分类问题将涉及两个或更多。

输入和输出之间的层被称为隐藏层,这就是神奇的地方!可以有任意数量的这些层,并且每层中的每个节点都接收前一层的所有输入值。然后,它为每个值分配一个随机权重乘数,将它们的总和相加,然后添加一个随机偏差值以确保区分( y = sum(wixi) + b )。然后,该值通过所谓的激活函数* ( ,其中有许多),然后返回一个值,由后续层中的每个节点处理。决定使用哪种激活函数取决于所处理的数据以及问题的类型。这些决定背后有一些复杂的数学原理,我不会在这里深入探讨,更多信息请查看 Sagar Sharma 的这篇伟大的 Medium 文章

数据通过这些层从左到右被处理的过程被称为正向传播。这些步骤随后被再次重复,重复次数由研究人员确定,称为批量。达到这个阈值后,网络反向工作,通过使用许多潜在的优化器方法之一调整权重和偏差,采取实际上最小化损失函数的步骤(又名学习,这是机器学习的学习方面!关于如何选择正确的优化器的更多细节,请查看 Sanket Doshi 的这篇伟大的文章。每次模型到达定型数据的末尾时,它都从开始处返回并继续。数据的每个完成的*‘圈’*被称为一个时期。

最后,当模型已经完成了所有这些步骤,并且运行了预定数量的时期时,那么可以说模型已经被’训练过,并且它准备好进行预测。哦,最后一点是关于我们如何比较模型的。当人们想要评估一个模型与另一个模型时,比方说比较某个任务的性能,用来表示模型大小和复杂性的一个重要术语是它拥有的参数的数量,因为计算单个层有时会很麻烦。考虑参数的一种方法是,想象如果我从一个输入节点开始,对每个节点遍历整个模型,会有多少条可能的路径。看看你能不能算出上图中有多少个参数。退一步说,这些数字可能会变得惊人!

干得好!你通过了神经网络的本质!我希望您现在已经理解了这种建模技术的基本过程。下面我将简要解释我用于图像处理的特定类型的神经网络,因为它有一些关键的差异,使它非常擅长具体解释图像数据。

卷积神经网络

如果你已经做到这一步,那么理解卷积神经网络( CNN )的细微差别应该相对简单。看看下图( Pic。看看你自己能不能理解 CNN 在做什么!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。一个基本的卷积神经网络结构。来源:走向数据科学。

你很有希望马上注意到的是,CNN 和基本的 MLP 架构之间的主要区别是在过程的前端。我们在这里看到的是该过程的卷积部分,这是将图像作为数据(由每个像素的 RGB 值表示)并将所谓的滤镜 应用于每个卷积层的行为。这些过滤器然后重新格式化输入数据,只保留看似重要的信息。当使用 CNN 时,这些信息通常被称为每幅图像的边缘特征。每个输入数据经过卷积步骤后,将作为列向量传递到最终的 MLP 结构,并在输出层进行分类,类似于上一节中讨论的过程。这个过程是专门为处理图像数据而设计的,因此它是当今图像处理的前沿方法之一。如需进一步阅读 CNN,请查看 Sumit Saha 的这篇文章

张量流、数据和模型性能

一种用于设计和执行神经网络的非常流行的技术被称为 张量流 。Tensorflow 是一个开源软件库, Google Brain 的第二代深度神经网络学习系统于 2017 年发布。Keras也是一个开源软件库,旨在实现深度神经网络的快速实验,运行在 Tensorflow 之上。

我用来训练我的模型的数据是由 Kaggle *提供的垃圾分类数据集。*该数据集包含超过 22,000 张标记图像,代表两个类别,或者是有机或者是回收利用**。大约有 12,000 个有机图像和 10,000 个回收图像,这意味着模型训练的类别平衡良好。检查以确保类之间存在健康的平衡是数据科学家为确保模型稳健而采取的许多重要预处理步骤之一。**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5。我的最佳表现模型的培训与验证损失和准确性。

所以我们在这里,经过大量的时间和努力微调我的 CNN,我终于能够建立和训练一个我满意的模型。

以下是我的 CNN 规格:

参数:670 万

测试损耗:~0.283

测试准确率:~91%

这些值显示在左侧(图 5 ),顶部的图形代表随时间的损失,底部代表精确度。如果你还记得,我们之前简单地讨论过纪元,正如你所看到的,这就是时间在 x 轴上的表现。

幸运的是,模型的训练如预期的那样进行,我们可以看到随着时间的推移,损失减少了,准确性也提高了。机器学习中存在许多计算限制,此链接详细介绍了这一主题。不过现在,请相信我的话,我说 50 个时期是在我的个人笔记本电脑上用这种规模的数据集调优和训练模型的好时间。总的来说,我对我的模型的表现很满意,因为我能够进入 90 年代,并且测试损失相对较低。

基准

数据科学家采用的典型基准测试实践是将您自己定制的模型与预先训练的模型进行比较。在 CNN 的世界中,有许多预先训练好的模型,Keras 已经内置了这些模型,允许轻松导入。

我决定将我的 CNN 与目前市场上表现最好的三个模型进行比较,即: VGG16VGG19除了**。这三款都脱胎于 ImageNet 挑战**。** ImageNet 的数据库中有超过 1500 万张图像,这些模型已经在其中的很大一部分上进行了预训练(我的数据集是 22,000 张图像,请记住关于计算能力的那部分… ),因此非常健壮或**“可概括”。这些模型是开源的,可以作为其他数据科学家在测试时调整/微调和比较他们自己的模型的基础。

行业精度基准:

VGG16: ~86%,基础参数:1470 万

VGG19: ~86%,基数参数:2000 万

异常:~90%,基础参数:2080 万

这里要注意的第一件事是这些预训练模型中的每一个与我的相比有多少参数。这代表了他们接受培训的大型数据集,以及创建这些模型的人投入的大量时间和精力来微调每一层。我们看到的 VGG16 和 19 的较低精度分数并不意味着我的模型在任何方面都‘更好’*,对于一个不是为我使用的特定数据集构建和训练的模型来说,这是一个非常好的分数。与我创建的模型相比,你将有更好的机会在新数据上测试这些预训练模型中的一个,并获得正确的预测,因为他们只是接触了更多的图像,尽管我支持我的模型,并欢迎挑战!最后,我们有例外模型,它的得分和我自己的模型一样好。关于这种特殊模型的一个注意事项是,它由非常非常多的层组成,但我们看到 VGG19 的参数数量相似…花一分钟时间,看看您是否能想到这可能是为什么(提示:记住要同时考虑层和节点!)。*

应用和未来工作

当谈到解决我们今天在全球面临的非常大和非常真实的废物管理挑战时,将所有这些付诸实践是真正的挑战。在家坐在电脑屏幕前就能获得这类结果,这很有趣,也几乎令人难以置信,但这些技术的实际应用才是问题的核心。幸运的是,已经有很多人致力于这些艰难的挑战。我的一个灵感来自于 Bobulski,Janusz & Kubanek,Mariusz的这篇论文。(2019).下图(图 6 )显示了他们提出的系统。当废物沿着传送带运送时,首先由固定的摄像机捕获图像,然后将数据传递到预先训练的 CNN 模型中,最后给出输出,然后通知空气喷射器将废物引导到哪个垃圾箱中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6。拟议的废物分类和分类系统。资料来源:Bobulski,Janusz & Kubanek,Mariusz。(2019).

这整个过程只需要几秒钟,而且可以连续运行几个小时。因此,从理论上讲,该系统可以提高整个垃圾分类过程的有效性(正确分类垃圾)和效率(能够更快和更长时间地分类)。

这个系统是一个真正的可能性,像这样的分类技术将依赖于在废物管理周期的所有阶段负责任地收集和解释数据,越来越接近循环经济的未来。

具体到我未来的工作,我很乐意沿着我已经开始的路线继续下去,访问越来越多的数据集,我可以在其上训练我的模型。然而,获得良好、全面的数据是一项挑战,也是传播此类技术的最大障碍之一。开发解决方案和实施这样的总体系统需要地方做出巨大的努力。在未来,我希望能够亲自为这些努力做出贡献,并在 2020 年 10 月从熨斗学校毕业后从事这一行业。

如果你能做到这一步,那么非常感谢你的时间!我希望这是翔实的,请随时联系我,如果你有任何问题或意见在 Tcastanley@gmail.com!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源

“使用神经网络预测股票价格”——不要被愚弄了

原文:https://towardsdatascience.com/using-neural-networks-to-predict-stock-prices-dont-be-fooled-c43a4e26ae4e?source=collection_archive---------15-----------------------

许多人声称他们可以使用机器学习来预测股票和加密货币的价格;但没人能证明靠实时数据盈利。有什么条件?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公共使用信贷:pixabay.com

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

如果你正在阅读这篇文章,你可能已经在网上看到过使用股票/加密货币数据和机器学习算法来“预测”未来价格的博客帖子/文章。在这里,我展示了一个类似的项目,其中我使用一些指标,如 Tweet 量、Google Trends 量、市值和交易量来“预测”明天的开盘价。在这个例子中,我们将关注比特币价格,因为这是一个热门话题,但只要数据可用,这可以外推至任何资产。有人可能会天真地认为,当有一天人们在推特上谈论比特币,在谷歌上搜索比特币,并比平时更多地交易比特币时,我们可能会看到比特币的价格明天会比今天更高。此外,也许我们可以根据这些数据以足够高的精度预测明天的开盘价,最终产生一个有利可图的交易算法。为了检验这一假设,我从经济学、谷歌趋势和其他免费在线资源中收集了一些数据,得出了以下数据框架。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从机器学习的角度来看,我们可以将这四个列视为我们的功能集,将“价格”列视为我们的目标。更具体地说,明天的价格是今天特性的目标。通过一点功能工程,我们还可以包括过去一天、过去两天、过去三天等指标的变化,其中大于 1 的值表示增加,小于 1 的值表示减少。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在单个实例中对最近的变化进行编码的工程特性

线性回归

现在我们有了数据集,让我们使用一些机器学习技术来看看我们是否可以准确预测明天的开盘价。

上面的代码使用 Scikit-Learn 执行简单的线性回归,并产生以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

预测价格与实际价格。R = 0.99008。RMSE=358.12

如果我们沿着时间轴绘制我们的预测价格和实际价格,我们观察到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一段时间内的预测价格和实际价格

哇!这太不可思议了!真的这么简单吗?你所要做的就是从网上获得一些搜索和交易量数据,并使用简单的线性回归,你就可以准确预测比特币的价格?不幸的是,事实并非如此。更不幸的是,网上有很多人展示这样的结果,并声称拥有神奇的快速致富交易算法。我每天在各种提要上看到的无数文章都使用这样的技巧,以及一些流行词汇,如“机器学习”、“人工智能”和“神经网络”,来抓住你的注意力,让你认为他们在做什么。你抓到窍门了吗?

这里的技巧是,昨天的价格被输入到具有特征集的模型中(或者通常类似于 avg last 5 days)。该模型很快了解到,将预测值固定在昨天的价格上,然后根据其他特征将其略微调高或调低,以尝试朝着正确的方向进行预测,这是最好的方法。一般来说,这个可能是一个不错的策略。例如,如果其他特征完美地指示了价格变动,这将非常有用。本质上,你的模型会使用昨天的价格作为基线,然后预测上涨或下跌。如果它能像许多人声称的那样准确预测涨跌,你就能快速致富!这里主要的、欺骗性的问题是在实际价格和预测价格之间使用像 R 或 RMSE 这样的指标来向人们展示“看我的模型预测得多好!”。任何有统计学背景的人都会将 0.99 的 R 值视为一个值得崇拜的模型。尽管在这种情况下,在训练数据中包含以前的价格保证了由残差评估的这种性能。我也见过文章/博客文章的标题照片是一个类似于上面显示的 plotly 输出的情节;此外,没有代码向读者显示以前的价格实际上包括在回归算法中。如果我运行同样的算法,但是用随机生成的值填充量要素,并且只包括今天的价格数据作为信息要素,那么从远处看输出将是相同的,R 也将非常高。实现这种把戏的一种更偷偷摸摸的方法是建立一个递归神经网络或 LSTM 模型,用 n 个以前的价格为网络的每个实例排序,然后不打乱训练数据。你告诉网络最小化损失,网络很快学会:“嘿,看,如果我用昨天的价格来预测,损失函数最小化!”。

回溯测试

为了进一步证明这种交易策略的盈利能力,我做了一些回溯测试,看看如果你真的用这些预测进行日常比特币交易会发生什么。下面是一个相似的岭回归模型的结果,该模型的 R 值也大于 0.99。如果您想查看回溯测试的完整细节和代码,我在本文末尾附上了完整笔记本的链接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的(令我失望),使用 Tweet 量、Google Trends、市值和交易量来预测明天的价格实际上比简单地在回溯测试间隔开始时买入和结束时卖出表现得更差,尽管模型评估指标非常出色。请记住,这还忽略了所有的交易费用,这显然会大大降低策略的性能。

话虽如此,我刚刚读了一篇由一所大学发表的学术博士撰写的文章,没有提及我概述的问题,但最终得出的结论是:“通过利用一个以推文和谷歌趋势数据为输入的线性模型,我们能够准确预测价格变化的方向。”[1]在论文的后面,他们提到“研究发现,逻辑回归在对这些推文进行分类方面表现最佳,它们能够正确预测 43.9%的价格上涨。”[1]嗯?我没看错吧?因此,作者在这里归类为“准确预测”的是,在不考虑费用的情况下,56.1%的时间亏损。哎哟。此外,在图 9 中,作者包括了一个类似于我上面展示的图(我在大约 20 分钟内生成的),其中预测价格精确地随着实际价格波动,以至于它们几乎不是不同的线。当然,没有提到昨天的价格在特性集中。

我想说明的是,将预测锁定在以前的价格,然后使用其他功能将预测向上或向下发送实际上可能是一种有用的交易策略(尽管在我看来,直接预测进场和出场比试图预测价格本身更好)。我写这篇文章的主要原因是,我认为许多人被这样的文章所欺骗,这些文章没有概述他们的训练数据的确切组成,而是试图通过在点击标题和封面照片的保护伞下只包括漂亮的情节和令人印象深刻的评估指标来打动读者。

结论

关于这个话题,我的想法是,即使这些在线文章看起来很吸引人,简单的机器学习算法并不是开发交易模型的最佳方法。我觉得这里有深度学习的地方;也许在强化学习风格的交易环境中,大型 LSTM、康文网络或其他具有技术分析类型特征的创新神经网络架构会产生多产的、有利可图的结果。最后,在研究算法交易之类的东西时,要记住几件事:

  1. 如果这看起来好得不像是真的,那很可能就是真的。再深入一点。寻找一个回溯测试,或者自己进行回溯测试。利润/损失是一个比 R 或 RMSE 更好的评估指标(参见我开发的简单回溯测试平台这里)。
  2. 当人们发现有利可图的算法时,他们一般不会发布到网上让全世界免费看到。每当你做交易时,总有人在交易的另一边。如果你赢了,他们就输了。如果你输了,他们赢了。那些成功者往往不会教育他们所资助的失败者。当然,除非你付钱让他们这么做。

这个项目的所有代码可以在这里找到复制。

引用:

[1]:使用推文量和情绪分析进行加密货币价格预测【https://scholar.smu.edu/cgi/viewcontent.cgi?article=1039 &context = datascience review

使用具有嵌入层的神经网络来编码高基数分类变量

原文:https://towardsdatascience.com/using-neural-networks-with-embedding-layers-to-encode-high-cardinality-categorical-variables-c1b872033ba2?source=collection_archive---------18-----------------------

我们如何使用具有数千个不同值的分类特征?

有多种方式对分类特征进行编码。如果类别之间不存在有序关系,one-hot-encoding 是一个受欢迎的候选(即,为每个类别添加一个二进制特征),还有和许多其他的。但是一次性编码有一些缺点——可以通过使用嵌入来解决。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

稍微有帮助的插图,图片由作者使用 draw.io 完成

一个缺点是,它不适合高基数类别:它将产生非常大/宽和稀疏的数据集,并且由于大量的要素,将需要大量的内存和正则化。此外,一次性编码没有利用类别之间的关系。假设你有动物种类作为特征,值类似于家猫老虎大象。与家猫大象相比,家猫老虎可能有更多的相似之处。这是一个更聪明的编码可以考虑的事情。在非常实际的场景中,这些类别可以是诸如客户、产品或位置之类的东西——非常高的基数类别,在各个观察之间具有相关的关系。

解决这些问题的一种方法是使用嵌入,一个流行的例子是 NLP 问题中的单词嵌入。我们使用更小的密集空间,而不是使用巨大的二进制空间对类别进行编码。我们不是手动编码它们,而是定义嵌入空间的大小,然后尝试让模型学习有用的表示。对于我们的动物物种示例,我们可以检索类似于代表家猫[1, 0.4, 0.1]代表老虎[0, 0.1, 0.6]代表大象[-0.5, 0.5, 0.4]代表鲨鱼等等的表示。

在接下来的文章中,我们将使用嵌入来构建一个神经网络来编码分类特征,此外,我们将针对一个没有分类变量的非常简单的线性模型和一个具有一次性编码特征的更复杂的正则化线性模型来对该模型进行基准测试。

玩具的例子

让我们看一个产生的玩具问题。假设我们反复从不同的供应商那里购买不同的产品,并且我们想要预测它们的大小。现在让我们假设每个产品都标有一个supplier_id和一个product_id,这是供应商和产品本身的标识符。我们还假设物品具有一些明显的尺寸/特征x1x2,如priceweight,以及一些秘密的、不可测量的特征s1s2s3,理论上可以这样计算出size(S3 对尺寸没有影响):

y = f(price, weight, s1, s2, s3) 
  = price + s1 + weight * s2

问题是我们不知道秘密特征s1s2s3,我们无法直接测量它们,这实际上是机器学习中一个相当常见的问题。但是我们在这里还有一点余地,因为我们有产品和供应商 id——但是数量太多了,无法一次性编码并直接使用它们。让我们从经验中假设我们知道来自不同卖家的产品具有不同的尺寸,因此有理由假设来自卖家的物品具有非常相似的秘密属性。

y = g(supplier_id, product_id, size, weight)

问题是,即使我们有几十万个不同的 id,我们的模型能从产品和供应商 id 中学习到上面的关系g吗?答案是肯定的,如果我们有足够的观测数据。

让我们看一个小数据集,以获得更好的图片。我们生成 300 个样本,在s1中有 4 个不同的值,在s2中有 3 个不同的值(记住s3没有影响),并可视化秘密属性对价格、重量和尺寸之间关系的明显影响。

import seaborn as sns
import matplotlib.pyplot as pltdata = generate_secret_data(n=300, s1_bins=3, s2_bins=6, s3_bins=2)
data.head(10)
##   s1   s2  s3  price  weight       y
## 0  1  0.0   2  1.269   2.089   4.055
## 1  3  2.0   1  2.412   1.283   9.764
## 2  2  1.0   2  3.434   1.010   8.230
## 3  1  3.0   1  4.493   1.837  12.791
## 4  3 -2.0   2  4.094   2.562   3.756
## 5  1  2.0   2  1.324   1.802   7.714
## 6  1  2.0   1  2.506   1.910   9.113
## 7  3 -2.0   1  3.626   1.864   4.685
## 8  2  1.0   1  2.830   2.064   8.681
## 9  1  2.0   1  4.332   1.100   9.319
g = sns.FacetGrid(data, col='s2', hue='s1', col_wrap=3, height=3.5);
g = g.map_dataframe(plt.scatter, 'weight', 'y');
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个 s2 属性的不同重量与尺寸(y)关系

g = sns.FacetGrid(data, col='s2', hue='s1', col_wrap=3, height=3.5); g = g.map_dataframe(plt.scatter, 'price', 'y'); 
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个 s2 物业的不同价格与规模(y)的关系

一个玩具的例子,它将如何出现在(一种)现实

但现在的问题是:我们不知道特性s1s2s3,只知道产品 id&供应商 id。为了模拟这个数据集在野外是如何出现的,让我们引入一个散列函数,我们将使用它来模糊我们不可测量的特性,并生成一些产品和供应商 id。

import hashlibdef generate_hash(*args):
    s = '_'.join([str(x) for x in args])
    return hashlib.md5(s.encode()).hexdigest()[-4:]generate_hash('a', 2)
## '5724'
generate_hash(123)
## '4b70'
generate_hash('a', 2)
## '5724'

我们现在可以用模糊属性生成数据,用产品 id 代替:

data = generate_data(n=300, s1_bins=4, s2_bins=1, s3_bins=2)
data.head(10)
##   product_id supplier_id  price  weight      y
## 0       7235        a154  2.228   2.287  4.470
## 1       9cb6        a154  3.629   2.516  8.986
## 2       3c7e        0aad  3.968   1.149  8.641
## 3       4184        0aad  3.671   2.044  7.791
## 4       4184        0aad  3.637   1.585  7.528
## 5       38f9        a154  1.780   1.661  4.709
## 6       7235        a154  3.841   2.201  6.040
## 7       efa0        0aad  2.773   2.055  4.899
## 8       4184        0aad  3.094   1.822  7.104
## 9       4184        0aad  4.080   2.826  8.591

我们仍然看到不同的产品倾向于具有不同的值,但是我们不再能够容易地从产品 id 计算出size值:

sns.relplot(
    x='price',
    y='y',
    hue='product_id',
    sizes=(40, 400),
    alpha=1,
    height=4,
    data=data
);plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个产品 id 的价格与尺寸(y)的关系—图片由作者制作

现在让我们生成一个更大的数据集。为了能够公平地将我们的嵌入模型与更简单的基线进行比较,并且为了能够验证我们的方法,我们将为我们的分类基数S1_BINSS2_BINSS3_BINS假设相当小的值。

如果S1_BINS, S2_BINS, S3_BINS >> 10000基准模型会遇到内存问题,性能会很差。

from sklearn.model_selection import train_test_splitN = 100000
S1_BINS = 30
S2_BINS = 3
S3_BINS = 50
data = generate_data(n=N, s1_bins=S1_BINS, s2_bins=S2_BINS, s3_bins=S3_BINS)data.describe()# cardinality of c1 is approx S1_BINS * S3_BINS,
# c2 is approx. S2_BINS * S3_BINS
##             price      weight           y
## count  100000.000  100000.000  100000.000
## mean        3.005       2.002      17.404
## std         0.997       0.502       8.883
## min        -1.052      -0.232      -2.123
## 25%         2.332       1.664       9.924
## 50%         3.004       2.002      17.413
## 75%         3.676       2.341      24.887
## max         7.571       4.114      38.641
data.describe(include='object')
##        product_id supplier_id
## count      100000      100000
## unique       1479         149
## top          8851        0d98
## freq          151        1376

我们现在将数据分为特征和响应,以及训练和测试。

x = data[['product_id', 'supplier_id', 'price', 'weight']]
y = data[['y']]
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=456)

构建基准模型:天真和基线

让我们首先组装一个非常简单的线性模型,它的性能非常差,并且只试图从priceweight中估计size:

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_errornaive_model = LinearRegression()
naive_model.fit(x_train[['price', 'weight']], y_train);y_pred_naive = naive_model.predict(x_test[['price', 'weight']])mean_squared_error(y_test, y_pred_naive)
## 77.63320758421973
mean_absolute_error(y_test, y_pred_naive)
## 7.586725358761727

如果我们观察priceweight与响应size之间的相关性,忽略 id,接近响应总体方差的不良性能是显而易见的:

sns.pairplot(data[['price', 'weight', 'y']].sample(1000));
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

价格、重量和尺寸(y)之间的相互关系—到处都是—图片由作者制作

为了获得更好的基准,我们可以对分类特性进行一次性编码,并标准化数字数据,使用sklearns ColumnTransformer将这些转换应用于不同的列。由于要素的数量,我们将使用岭回归而不是正常的线性回归来保持系数较小(但非零,不像 Lasso,这将导致丢失特定类的信息)。

from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformerdef create_one_hot_preprocessor():
    return ColumnTransformer([
        ('one_hot_encoder', OneHotEncoder(sparse=False, handle_unknown='ignore'), ['product_id', 'supplier_id']),
        ('standard_scaler', StandardScaler(), ['price', 'weight'])]
    )

one hot 预处理器将特征扩展到列并使数据变宽,此外,数字特征标准化为零均值和单位方差:

建立具有嵌入层的神经网络,包括预处理和未知类别处理

现在让我们为我们的类别建立一个具有嵌入层的神经网络模型。为了将它们提供给嵌入层,我们需要首先将分类变量映射到数字序列,即分别来自区间[0, #supplier ids]的整数。[0, #product ids]

由于sklearns OrdinalEncoder目前还不能处理未知值,我们需要随机应变。当随机分割测试数据或在预测期间看到新数据时,可能会出现未知类别。因此,我们必须使用编码器的简单实现(使用数据帧而不是数组,没有针对速度进行优化),它可以处理未知值:我们本质上使用有序字典作为散列表来将值映射到正整数范围,其中未知值将被映射到 0(我们需要映射到非负值以符合稍后的嵌入层)。

编码器现在可以编码和解码我们的数据:

ce = ColumnEncoder()ce.fit_transform(x_train)
##        product_id  supplier_id  price  weight
## 17414         941          104  2.536   1.885
## 54089         330          131  3.700   1.847
## 84350         960          122  3.517   2.341
## 68797         423           77  4.942   1.461
## 50994         617          138  4.276   1.272
## ...           ...          ...    ...     ...
## 55338         218          118  2.427   2.180
## 92761         528           10  1.705   1.368
## 48811         399           67  3.579   1.938
## 66149         531          126  2.216   2.997
## 30619        1141           67  1.479   1.888
## 
## [75000 rows x 4 columns]
ce.inverse_transform(ce.transform(x_train))
##       product_id supplier_id  price  weight
## 17414       a61d        b498  2.536   1.885
## 54089       36e6        e41f  3.700   1.847
## 84350       a868        d574  3.517   2.341
## 68797       4868        80cf  4.942   1.461
## 50994       69f3        eb54  4.276   1.272
## ...          ...         ...    ...     ...
## 55338       2429        cc4a  2.427   2.180
## 92761       5c02        0ec5  1.705   1.368
## 48811       426c        7a7d  3.579   1.938
## 66149       5d45        dc6f  2.216   2.997
## 30619       c73d        7a7d  1.479   1.888
## 
## [75000 rows x 4 columns]
x_train.equals(ce.inverse_transform(ce.transform(x_train)))
## True

它还可以通过将未知数据映射到零类别来处理未知数据:

unknown_data = pd.DataFrame({
    'product_id': ['!§$%&/()'],
    'supplier_id': ['abcdefg'],
    'price': [10],
    'weight': [20],
  })ce.transform(unknown_data)
##    product_id  supplier_id  price  weight
## 0           0            0     10      20
ce.inverse_transform(ce.transform(unknown_data))
##   product_id supplier_id  price  weight
## 0       None        None     10      20

为了将数据输入到模型中,我们需要分割输入,将其传递到不同的层,本质上是传递到X = [X_embedding1, X_embedding2, X_other]。我们可以再次使用转换器来实现这一点,这次使用的是np.arrays,因为StandardScaler返回数组:

emb = EmbeddingTransformer(cols=[0, 1])
emb.fit_transform(x_train.head(5))
## [array([['a61d'],
##        ['36e6'],
##        ['a868'],
##        ['4868'],
##        ['69f3']], dtype=object), array([['b498'],
##        ['e41f'],
##        ['d574'],
##        ['80cf'],
##        ['eb54']], dtype=object), array([[2.5360678952988436, 1.8849677601403312],
##        [3.699501628053666, 1.8469279753798342],
##        [3.5168780519630527, 2.340554963373134],
##        [4.941651644756232, 1.4606898248596456],
##        [4.27624682317603, 1.2715509823965785]], dtype=object)]

现在让我们将这两者结合起来,用训练数据装配预处理器,对类别进行编码,执行缩放并将其转换为正确的格式:

def create_embedding_preprocessor():
  encoding_preprocessor = ColumnTransformer([
      ('column_encoder', ColumnEncoder(), ['product_id', 'supplier_id']),
      ('standard_scaler', StandardScaler(), ['price', 'weight'])
  ])embedding_preprocessor = Pipeline(steps=[
      ('encoding_preprocessor', encoding_preprocessor),
      # careful here, column order matters:
      ('embedding_transformer', EmbeddingTransformer(cols=[0, 1])),
  ])return embedding_preprocessorembedding_preprocessor = create_embedding_preprocessor()
embedding_preprocessor.fit(x_train);

如果我们现在将这些数据提供给模型,它将无法学习未知类别的任何合理内容,即当我们进行fit时,在训练数据x_train中不存在的类别。因此,一旦我们试图对这些进行预测,我们可能会得到不合理的估计。

解决这个词汇外问题的一种方法是将一些随机的训练观察值设置到未知的类别中。因此,在模型的训练期间,转换将使用 0 对这些进行编码,这是未知类别的标记,并且它将允许模型学习接近未知类别的平均值的东西。有了更多的领域知识,我们还可以选择任何其他类别作为默认类别,而不是随机抽样。

# vocab sizes
C1_SIZE = x_train['product_id'].nunique()
C2_SIZE = x_train['supplier_id'].nunique()x_train = x_train.copy()
n = x_train.shape[0]# set a fair share to unknown
idx1 = np_random_state.randint(0, n, int(n / C1_SIZE))
x_train.iloc[idx1,0] = '(unknown)'idx2 = np_random_state.randint(0, n, int(n / C2_SIZE))
x_train.iloc[idx2,1] = '(unknown)'x_train.sample(10, random_state=1234)
##       product_id supplier_id  price  weight
## 17547       7340        6d30  1.478   1.128
## 67802       4849        f7d5  3.699   1.840
## 17802       de88        55a0  3.011   2.306
## 36366       0912        1d0d  2.453   2.529
## 27847       f254        56a6  2.303   2.762
## 19006       2296   (unknown)  2.384   1.790
## 34628       798f        5da6  4.362   1.775
## 11069       2499        803f  1.455   1.521
## 69851       cb7e        bfac  3.611   2.039
## 13835       8497        33ab  4.133   1.773

我们现在可以转换数据

x_train_emb = embedding_preprocessor.transform(x_train)
x_test_emb = embedding_preprocessor.transform(x_test)x_train_emb[0]
## array([[ 941.],
##        [ 330.],
##        [ 960.],
##        ...,
##        [ 399.],
##        [ 531.],
##        [1141.]])
x_train_emb[1]
## array([[104.],
##        [131.],
##        [122.],
##        ...,
##        [ 67.],
##        [126.],
##        [ 67.]])
x_train_emb[2]
## array([[-0.472, -0.234],
##        [ 0.693, -0.309],
##        [ 0.51 ,  0.672],
##        ...,
##        [ 0.572, -0.128],
##        [-0.792,  1.976],
##        [-1.53 , -0.229]])

是时候建立神经网络了!我们有 3 个输入(2 个嵌入,1 个正常)。嵌入输入都被传递到嵌入层,被展平并与正常输入连接。下面的隐藏层由 Dense & Dropout 组成,最后线性激活。

import tensorflow as tfmodel = create_model(embedding1_vocab_size=C1_SIZE+1, 
                     embedding2_vocab_size=C2_SIZE+1)tf.keras.utils.plot_model(
    model,
    to_file='../../static/img/keras_embeddings_model.png',
    show_shapes=True,
    show_layer_names=True,
)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有嵌入层的神经网络,图像由作者使用 tensor flow . keras . utils . plot _ model 完成

num_epochs = 50model.fit(
    x_train_emb,
    y_train,
    validation_data=(x_test_emb, y_test),
    epochs=num_epochs,
    batch_size=64,
    verbose=0,
);

如果您看到类似InvalidArgumentError: indices[37,0] = 30 is not in [0, 30)的错误,则您选择了错误的词汇表大小,根据文档,它应该是嵌入值的最大索引加 1。

正如我们所看到的,对于我们的线性问题,该模型的表现与基线线性模型相当好,甚至更好,尽管它的真正优势只有在我们扩大类别空间或向响应添加非线性时才会发挥作用:

y_pred_emb = model.predict(x_test_emb)mean_squared_error(y_pred_baseline, y_test)
## 0.34772562548572583
mean_squared_error(y_pred_emb, y_test)
## 0.2192712407711225
mean_absolute_error(y_pred_baseline, y_test)
## 0.3877675893786461
mean_absolute_error(y_pred_emb, y_test)
## 0.3444112401247173

我们还可以提取嵌入层的权重(第一行包含零标签的权重):

weights = model.get_layer('embedding1').get_weights()
pd.DataFrame(weights[0]).head(11)
##         0      1      2
## 0  -0.019 -0.057  0.069
## 1   0.062  0.059  0.014
## 2   0.051  0.094 -0.043
## 3  -0.245 -0.330  0.410
## 4  -0.224 -0.339  0.576
## 5  -0.087 -0.114  0.324
## 6   0.003  0.048  0.093
## 7   0.349  0.340 -0.281
## 8   0.266  0.301 -0.275
## 9   0.145  0.179 -0.153
## 10  0.060  0.050 -0.049

这些权重有可能被保存在某个地方,并在其他模型中用作特征(预训练嵌入)。查看前 10 个类别,我们可以看到来自supplier_id的具有相似响应的值y具有相似的权重:

column_encoder = (embedding_preprocessor
  .named_steps['encoding_preprocessor']
  .named_transformers_['column_encoder'])data_enc = column_encoder.transform(data)(data_enc
  .sort_values('supplier_id')
  .groupby('supplier_id')
  .agg({'y': np.mean})
  .head(10))
##                   y
## supplier_id        
## 1            19.036
## 2            19.212
## 3            17.017
## 4            15.318
## 5            17.554
## 6            19.198
## 7            17.580
## 8            17.638
## 9            16.358
## 10           14.625

此外,该模型对于未知数据表现合理,因为其表现类似于我们非常简单的基线模型,也类似于假设简单的条件均值:

unknown_data = pd.DataFrame({
    'product_id': ['!%&/§(645h'],
    'supplier_id': ['foo/bar'],
    'price': [5],
    'weight': [1]
})np.mean(y_test['y'])
## 17.403696362314747# conditional mean
idx = x_test['price'].between(4.5, 5.5) & x_test['weight'].between(0.5, 1.5)
np.mean(y_test['y'][idx])
## 18.716701011038868# very naive baseline
naive_model.predict(unknown_data[['price', 'weight']])
## array([[18.905]])# ridge baseline
baseline_pipeline.predict(unknown_data)
## array([[18.864]])# embedding model
model.predict(embedding_preprocessor.transform(unknown_data))
## array([[19.045]], dtype=float32)

包裹

我们已经看到了如何利用嵌入层来编码高基数分类变量,并且根据基数,我们还可以调整密集特征空间的维度以获得更好的性能。这样做的代价是一个更加复杂的模型,而不是运行一个带有一次性编码的经典 ML 方法。
如果经典模型是优选的,则类别权重可以从嵌入层中提取,并在更简单的模型中用作特征,因此取代了一次性编码步骤。

最初发表于

使用 NLP 探索领导力和灵感

原文:https://towardsdatascience.com/using-nlp-to-explore-leadership-inspiration-f2e0b805d01c?source=collection_archive---------37-----------------------

对 NPR 前 350 名毕业典礼演讲进行 EDA、主题建模和情感分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MD 杜兰Unsplash 上的照片

每年,全国各地的领导人都会站出来,通过充满情感、政治和鼓舞人心的毕业典礼演讲来激励下一代。随着今年演讲的临近,我决定探究这些领导人如何激励年轻人,以及性别是否在他们的方法中发挥了作用。

设计

为了建立我的语料库,我开始用 Beautiful Soup 搜集 NPR 排名前 350 的毕业典礼演讲的名字、年份和学校。演讲本身的获取有点棘手,因为它们存在于许多不同的格式中,包括 pdf 文档、各种新闻文章和大学网站。因此,我没有直接从 NPR 提供的链接中抓取演讲稿,而是用 Selenium 构建了一个刮刀,从 YouTube 视频中抓取文稿。由于缺少了一些演讲,我最终得到了一个超过 300 个文档的完整语料库。

利用收集的数据,我使用自然语言处理(NLP)来了解男性和女性领导者如何使用毕业典礼演讲平台。首先,我手动创建了一个男性或女性的二元列,然后使用 ed a、主题建模和情感分析来识别演讲和性别的模式。在这里找到的完整代码

调查的结果

探索性数据分析

和埃达单独在一起时,我能够开始看到毕业典礼演讲中的模式。把这些演讲分成一个单词列表,我就可以比较每个演讲的长度。虽然大多数男性和女性的演讲长度在 2000-3000 字之间,但男性的演讲比女性的演讲更长。男性演讲的平均长度为 3068 个单词,而女性演讲的平均长度为 2537 个单词,这相当于 1-2 个段落的差异。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由 Molly Liebeskind 创建的图像

我还创建了仅包含动词、仅包含名词和仅包含形容词的栏目,并发现词性的平均分布在男性和女性的演讲中是一致的。

主题建模

在探索了各种主题建模技术和矢量器之后,我确定这个问题最强的方法是使用 TF-IDF 矢量器引理化非负矩阵分解。男性和女性演讲者的演讲可以分为五个主题:教育、文化、职业、希望、政治

主题符合我对毕业典礼演讲的期望。然而,有趣的是,我发现男性和女性演讲者的主题分布有所不同。为了形成下面的图表,我们根据演讲中最常见的主题对每篇演讲进行了分类。例如,如果一个演讲主要是关于政治,但也有一点关于职业,那么它就被归类为政治。如下图所示,男性演讲者更重视职业和政治,而女性演讲者更重视教育和文化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由 Molly Liebeskind 创建的图像

男性和女性领导人都谈到了希望、未来和抱负。

情感分析

我的下一个目标是确定毕业典礼演讲是否有一个一致的故事情节或公式。为了做到这一点,我首先将每篇演讲分成 10 个更小的部分,这将允许我观察整个演讲过程中情绪的变化。然后,使用维德情绪分析,我确定了每个部分的积极程度。我发现,在激励毕业生时,无论男女,演讲者都遵循一种一致的模式。他们以强烈的正面陈述开始,引发了兴趣和兴奋。他们利用演讲的主体来谈论他们的激情点,包括政治、教育改革、气候变化等。最后,他们以对下一代未来的鼓舞和兴奋而结束。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由 Molly Liebeskind 创建的图像

结论

大学毕业典礼是全美领导人与年轻一代分享智慧的时刻。根据演讲者的不同,学生将会接触到关于政治、教育、职业或文化的建议。然而,不管演讲者是谁,学生们都被充满希望、改变和进化的话语所鼓舞和激励。

我个人最喜欢的几个:

  • 比尔·盖茨在哈佛大学
  • 杜兰大学的艾伦·德杰尼勒斯
  • 巴拉克·欧巴马在霍华德大学
  • 肯扬学院的戴维·福斯特·华莱士

请在下面留下任何问题和评论!

Python 中的自然语言处理和观点挖掘

原文:https://towardsdatascience.com/using-nlp-to-figure-out-what-people-really-think-e1d10d98e491?source=collection_archive---------20-----------------------

Rayshard Brooks 枪击案的情感分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源

在本文中,我将向您展示如何使用自然语言处理(NLP)和更具体的情感分析来了解人们对某个主题的真实感受。

背景

我决定分析 Youtube 上与以下视频相关的评论:【https://www.youtube.com/watch?v=kuhhT_cBtFU】T2&t = 5s。

美国有线电视新闻网(CNN)发布的视频显示了 2020 年 6 月 12 日亚特兰大一名警察开枪打死 Rayshard Brooks 的瞬间。Rayshard Brooks 因涉嫌酒后驾车被捕,并试图逃离警察。他设法偷了一把泰瑟枪,并试图在逃跑时用它射杀一名警察。然而,美国有线电视新闻网没有显示争吵的最后一部分(在另一台摄像机上捕捉到的),但他们很快提到了它。

我很想知道人们会对整个事件以及 CNN 遗漏部分事件做出什么样的反应。非常清楚,我不是在这里给出我的观点,我只是在分析关于这个事件的说法。

获取数据

我用 selenium 搜集了 Youtube 上的评论。我总共收集了 1460 条评论。下面这篇文章展示了我是如何做到的:https://towards data science . com/how-to-scrape-YouTube-comments-with-python-61ff 197115d 4

数据如下所示:

df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清理数据

这是任何 NLP 项目的重要组成部分。以下是用于清理文本的函数:

**#Import packages** import pandas as pd
import re, string
import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer 
from textblob import TextBlobsw = stopwords.words('english')**#The function** def clean_text(text):text = text.lower()
    text = re.sub('@', '', text)
    text = re.sub('\[.*?\]', '', text)
    text = re.sub('https?://\S+|www\.\S+', '', text)
    text = re.sub('<.*?>+', '', text)
    text = re.sub('[%s]' % re.escape(string.punctuation), '', text)
    text = re.sub('\n', '', text)
    text = re.sub('\w*\d\w*', '', text)
    text = re.sub(r"[^a-zA-Z ]+", "", text)

 **#Tokenize the data**    text = nltk.word_tokenize(text)
 **#Remove stopwords**    text = [w for w in text if w not in sw]return text

该函数的作用如下:

  • 删除所有大写字母、标点符号、表情符号、链接等。基本上,去掉所有不是单词或数字的东西。
  • 将数据符号化成单词,这意味着将每个注释分解成一组单独的单词。
  • 删除所有停用词,这些词不会给评论增加价值,比如“the”、“a”、“and”等。

现在让我们将函数应用于数据:

df['comment'] = df['comment'].apply(lambda x: clean_text(x))

词汇化

我决定对文本进行词汇化,这是将一个单词的屈折形式组合在一起的过程,这样它们就可以作为一个单独的项目进行分析,因为它们有相似的意思(walking 变成 walk,officers 变成 officer,等等)。).

nltk。WordNetLemmatizer()函数就是这样做的。代码如下:

**#Lemmatizer** lemmatizer = WordNetLemmatizer()def lem(text):
    text = [lemmatizer.lemmatize(t) for t in text]
    text = [lemmatizer.lemmatize(t, 'v') for t in text]
    return textdf['comment'] = df['comment'].apply(lambda x: lem(x))

最后我把数据里所有的空评论都去掉了(有些人只是评论表情符号,标点符号之类的东西)。

**#Remove all empty comments** empty_comment = df['comment'][1459]for i in range(len(df)):
    if df['comment'][i]==empty_comment:
        df=df.drop(i)df=df.reset_index(drop=True)

这留给我们 1441 条评论来分析。

分析数据

字频率

让我们使用 nltk 的 FreqDist 函数,从查看词频开始,也就是说,哪些词在评论中出现的频率最高。

**#From lists of comments to a single list containing all words** all_words=[]        
for i in range(len(df)):
    all_words = all_words + df['comment'][i]**#Get word frequency** nlp_words = nltk.FreqDist(all_words)
plot1 = nlp_words.plot(20, color='salmon', title='Word Frequency')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这并不能让我们了解人们对这个视频的感受。不过,这里还是有一些值得一提的地方:

  • 种族主义这个词被说了 17 次,种族主义这个词被说了 21 次,而残暴这个词只被说了 6 次。
  • 罪犯这个词被写了 40 次。

让我们寻找最频繁的二元组,这意味着在评论中最频繁的相邻词对。

#**Bigrams**
bigrm = list(nltk.bigrams(all_words))
words_2 = nltk.FreqDist(bigrm)
words_2.plot(20, color='salmon', title='Bigram Frequency')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好吧,好吧,现在我们开始看到一些有趣的东西。“拒捕”这个词出现得最多。这里还大量提到了泰瑟枪(射泰瑟枪、拿泰瑟枪、警泰瑟枪等),即使片段中没有显示显示雷夏德·布鲁克斯(Rayshard Brooks)发射泰瑟枪的视频。著名的“假新闻”也出现了差不多 30 次。

以下是最受欢迎的三元模型图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我认为这个图表真正突出了人们的想法。很多假新闻和很多关于使用被盗泰瑟枪的报道。还提到了使用致命武力。

极性

使用 Textblob 的情感函数,我们可以查看评论的极性,这是一个介于-1 和 1 之间的浮点数,用于表示评论是正面(1)还是负面(-1)。例如,句子“Textblob 很棒”的极性为 0.4。

以下是如何获得每个评论的极性以及所述极性的分布:

**#Get sentiment from comments** df['comment'] = [str(thing) for thing in df['comment']]sentiment = []
for i in range(len(df)):
    blob = TextBlob(df['comment'][i])
    for sentence in blob.sentences:
        sentiment.append(sentence.sentiment.polarity)df['sentiment']=sentiment**#Plot** df['sentiment'].plot.hist(color='salmon', title='Comments Polarity')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绝大多数评论被认为是相当中性的,平均评论的极性为-0.005。大约 75%的评论极性小于 0.06,意味着正面评论很少(我当然希望如此,毕竟是枪击案!).事实上,1441 条评论中只有 45 条的极性超过 0.5。

尽管如此,我认为可以从这一分析中得出以下几点结论:

  • 人们不喜欢 CNN 遗漏了 Rayshard Brooks 开枪的片段,称该视频和 CNN 经常是“假新闻”。
  • Rayshard Brooks 的行为似乎在这里被负面地看待,许多人提到他酒后驾车,拒捕,试图逃跑并开枪。
  • 视频观众似乎认为枪击发生的条件更“可接受”,这可以解释中性的极性。
  • 令人惊讶的是很少提及警察暴行或种族主义。

这就是了。我们设法了解了人们对 CNN 发布的视频和发生的事件的感受。

评论分类

在这种情况下,很难对评论进行分类(例如正面和负面),因为我们不知道评论的“真实”类别。我们没有已知的标签来构建和评估一个简单的机器学习分类器。

然而,有标记文本数据的方法(手动、使用极性、使用正对负单词的计数等。),我将在下一篇文章中介绍。但是请记住,如果我们不知道注释的真实类别,就永远不可能知道模型的真实性能。这也是 NLP 如此具有挑战性的原因之一!

非常感谢你的阅读!

完整的代码可以在这里找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值