使用 Neo4j 进行链接预测第 2 部分:使用 scikit-learn 预测合著者
这是最近添加到 Neo4j 图形算法库的链接预测函数系列文章的第二篇。
Link Predictions in the Neo4j Graph Algorithms Library
在第一篇帖子中,我们学习了链接预测度量,如何在 Neo4j 中应用它们,以及如何在机器学习分类器中用作特征。我们还了解了在处理图形时拆分训练和测试数据集的挑战。
在本帖中,我们将应用我们在引用数据集中学到的东西。我们将使用 scikit-learn 和链接预测算法来预测未来的合作关系。
Amy Hodler 和我在上周的 Neo4j 在线会议上展示了如何应用本文中描述的方法,所以你也可以观看视频。
Link Prediction Online Meetup
如果你还在等待书面版本,那就让我们开始吧!
导入引用数据集
我们将使用来自 DBLP 引文网络的数据,其中包括来自各种学术来源的引文数据。我写了一篇博文解释了如何导入完整的数据集,但是在这篇博文中,我们将关注来自一些软件开发会议的数据。
Citation Networks
我们可以通过运行以下 Cypher 语句来导入该数据子集。只要在 Neo4j 浏览器中启用了多语句编辑器,就可以一次性运行所有语句:
// Create constraints
CREATE CONSTRAINT ON (a:Article) ASSERT a.index IS UNIQUE;
CREATE CONSTRAINT ON (a:Author) ASSERT a.name IS UNIQUE;
CREATE CONSTRAINT ON (v:Venue) ASSERT v.name IS UNIQUE;// Import data from JSON files using the APOC library
CALL apoc.periodic.iterate(
'UNWIND ["dblp-ref-0.json", "dblp-ref-1.json", "dblp-ref-2.json", "dblp-ref-3.json"] AS file
CALL apoc.load.json("[https://github.com/mneedham/link-prediction/raw/master/data/](https://github.com/mneedham/link-prediction/raw/master/data/)" + file)
YIELD value WITH value
RETURN value',
'MERGE (a:Article {index:value.id})
SET a += apoc.map.clean(value,["id","authors","references", "venue"],[0])
WITH a, value.authors as authors, value.references AS citations, value.venue AS venue
MERGE (v:Venue {name: venue})
MERGE (a)-[:VENUE]->(v)
FOREACH(author in authors |
MERGE (b:Author{name:author})
MERGE (a)-[:AUTHOR]->(b))
FOREACH(citation in citations |
MERGE (cited:Article {index:citation})
MERGE (a)-[:CITED]->(cited))',
{batchSize: 1000, iterateList: true});
下图显示了数据导入 Neo4j 后的样子:
Diagram showing Citation Network in Neo4j
构建合著者图表
数据集不包含描述他们合作的作者之间的关系,但我们可以根据找到的多人撰写的文章来推断它们。
下面的 Cypher 语句在至少合作过一篇文章的作者之间创建了一个CO_AUTHOR
关系:
MATCH (a1)<-[:AUTHOR]-(paper)-[:AUTHOR]->(a2:Author)
WITH a1, a2, paper
ORDER BY a1, paper.year
WITH a1, a2, collect(paper)[0].year AS year,
count(*) AS collaborations
MERGE (a1)-[coauthor:CO_AUTHOR {year: year}]-(a2)
SET coauthor.collaborations = collaborations;
我们在已经合作的作者之间只创建一个关系,即使他们已经合作了多篇文章。我们在这些关系上创建了几个属性:
- 一个
year
属性,表示作者合作的第一篇文章的出版年份 - 一个
collaborations
属性,指示作者合作了多少篇文章
Diagram showing co-authorsin Neo4j
现在我们已经有了合著者图表,我们需要弄清楚如何预测作者之间未来的合作。
为此,我们将构建一个二元分类器,因此我们的下一步是创建训练和测试图。
训练和测试数据集
正如在第一篇文章中提到的,我们不能随意将数据分成训练和测试数据集,因为这可能导致数据泄漏。
当您的训练数据之外的数据被无意中用于创建您的模型时,可能会发生数据泄漏。这在处理图形时很容易发生,因为我们的训练集中的节点对可能连接到测试集中的节点对。
相反,我们需要将我们的图分成训练和测试子图,幸运的是,我们的引用图包含我们可以分割的时间信息。我们将通过拆分特定年份的数据来创建训练和测试图。
但是我们应该在哪一年分手呢?我们来看看合著者合作的第一年的分布情况:
Chart showing distribution of year of collaboration
看起来 2006 年是拆分数据的好年份,因为它将为我们的每个子图提供合理数量的数据。我们将把 2005 年及更早的所有合作者作为我们的训练图,而从 2006 年开始的所有合作者作为测试图。
让我们基于该年在图表中创建明确的CO_AUTHOR_EARLY
和CO_AUTHOR_LATE
关系。以下代码将为我们创建这些关系:
列车子图
MATCH (a)-[r:CO_AUTHOR]->(b)
WHERE r.year < 2006
MERGE (a)-[:CO_AUTHOR_EARLY {year: r.year}]-(b);
测试子图
MATCH (a)-[r:CO_AUTHOR]->(b)
WHERE r.year >= 2006
MERGE (a)-[:CO_AUTHOR_LATE {year: r.year}]-(b);
这种分裂在早期图表中留给我们 81,096 个关系,在后期图表中留给我们 74,128 个关系。这是 52 比 48 的比例。这比我们通常在测试图中看到的数值百分比要高,但是应该没问题。
这些子图中的关系将作为我们训练和测试集中的正例,但是我们也需要一些反例。需要反面的例子,这样我们的模型就可以学习区分应该有链接的节点和不应该有链接的节点。
正如在链接预测问题中经常出现的情况一样,负面例子比正面例子多得多。反例的最大数量等于:
# negative examples = (# nodes)² - (# relationships) - (# nodes)
即节点数的平方,减去图中的关系,减去自身关系。
我们不是使用几乎所有可能的节点对,而是使用彼此相距 2 到 3 跳的节点对。这将为我们提供更多可管理的数据。
我们可以通过运行以下查询来生成这些对:
MATCH (author:Author)
WHERE (author)-[:CO_AUTHOR_EARLY]-()
MATCH (author)-[:CO_AUTHOR_EARLY*2..3]-(other)
WHERE not((author)-[:CO_AUTHOR_EARLY]-(other))
RETURN id(author) AS node1, id(other) AS node2
这个查询返回了 4389478 个负面例子,而 81096 个正面例子,这意味着我们有 54 倍于的负面例子。
Imbalanced data
因此,我们仍然有很大的类别不平衡,这意味着预测每对节点都不会有链接的模型将非常准确。
为了解决这个问题,我们可以对正样本进行上采样,或者对负样本进行下采样。我们将采用下采样方法。
py2neo,熊猫,scikit-学习
在这篇文章的剩余部分,我们将用 Python 处理 py2neo 、 pandas 和 scikit-learn 库。
py2neo 驱动程序使数据科学家能够轻松地将 Neo4j 与 Python 数据科学生态系统中的工具集成。我们将使用这个库对 Neo4j 执行 Cypher 查询。
pandas 是一个开源的、BSD 许可的库,为 Python 编程语言提供了高性能、易于使用的数据结构和数据分析工具
scikit-learn 是一个流行的机器学习库。我们将使用这个库来构建我们的机器学习模型。
我们可以从 PyPi 安装这些库:
pip install py2neo==4.1.3 pandas sklearn
一旦我们安装了这些库,我们将导入所需的包,并创建一个数据库连接:
from py2neo import Graph
import pandas as pdgraph = Graph("bolt://localhost", auth=("neo4j", "neo4jPassword"))
构建我们的训练和测试集
我们现在可以编写以下代码来创建一个包含基于早期图的正面和反面例子的测试数据帧:
# Find positive examples
train_existing_links = graph.run("""
MATCH (author:Author)-[:CO_AUTHOR_EARLY]->(other:Author)
RETURN id(author) AS node1, id(other) AS node2, 1 AS label
""").to_data_frame()# Find negative examples
train_missing_links = graph.run("""
MATCH (author:Author)
WHERE (author)-[:CO_AUTHOR_EARLY]-()
MATCH (author)-[:CO_AUTHOR_EARLY*2..3]-(other)
WHERE not((author)-[:CO_AUTHOR_EARLY]-(other))
RETURN id(author) AS node1, id(other) AS node2, 0 AS label
""").to_data_frame()# Remove duplicates
train_missing_links = train_missing_links.drop_duplicates()# Down sample negative examples
train_missing_links = train_missing_links.sample(
n=len(train_existing_links))# Create DataFrame from positive and negative examples
training_df = train_missing_links.append(
train_existing_links, ignore_index=True)
training_df['label'] = training_df['label'].astype('category')
Sample of the training DataFrame
现在我们将做同样的事情来创建一个测试数据帧,但是这次我们只考虑后期图中的关系:
# Find positive examples
test_existing_links = graph.run("""
MATCH (author:Author)-[:CO_AUTHOR_LATE]->(other:Author)
RETURN id(author) AS node1, id(other) AS node2, 1 AS label
""").to_data_frame()# Find negative examples
test_missing_links = graph.run("""
MATCH (author:Author)
WHERE (author)-[:CO_AUTHOR_LATE]-()
MATCH (author)-[:CO_AUTHOR_LATE*2..3]-(other)
WHERE not((author)-[:CO_AUTHOR_LATE]-(other))
RETURN id(author) AS node1, id(other) AS node2, 0 AS label
""").to_data_frame()# Remove duplicates
test_missing_links = test_missing_links.drop_duplicates()# Down sample negative examples
test_missing_links = test_missing_links.sample(n=len(test_existing_links))# Create DataFrame from positive and negative examples
test_df = test_missing_links.append(
test_existing_links, ignore_index=True)
test_df['label'] = test_df['label'].astype('category')
现在是时候创建我们的机器学习模型了。
选择机器学习算法
我们将创建一个随机森林分类器。这种方法非常适合,因为我们的数据集将由强特征和弱特征混合组成。虽然弱特征有时会有所帮助,但随机森林方法将确保我们不会创建过度适合我们的训练数据的模型。
我们可以用下面的代码创建这个模型:
from sklearn.ensemble import RandomForestClassifierclassifier = RandomForestClassifier(n_estimators=30, max_depth=10,
random_state=0)
现在是时候设计一些特征来训练我们的模型了。
特征提取是一种从大量数据和属性中提取出一组有代表性的数值,即特征的方法。然后将其用作输入数据,这样我们就可以区分学习任务的类别/值。
请记住,如果您想在这篇文章的下一部分试用代码示例,您需要确保您已经按照本系列文章的第篇文章中所述设置了 Neo4j 开发环境。
生成链接预测要素
我们将首先使用链接预测函数创建一些要素
def apply_graphy_features(data, rel_type):
query = """
UNWIND $pairs AS pair
MATCH (p1) WHERE id(p1) = pair.node1
MATCH (p2) WHERE id(p2) = pair.node2
RETURN pair.node1 AS node1,
pair.node2 AS node2,
algo.linkprediction.commonNeighbors(
p1, p2, {relationshipQuery: $relType}) AS cn,
algo.linkprediction.preferentialAttachment(
p1, p2, {relationshipQuery: $relType}) AS pa,
algo.linkprediction.totalNeighbors(
p1, p2, {relationshipQuery: $relType}) AS tn
"""
pairs = [{"node1": pair[0], "node2": pair[1]}
for pair in data[["node1", "node2"]].values.tolist()]
params = {"pairs": pairs, "relType": rel_type}
features = graph.run(query, params).to_data_frame()
return pd.merge(data, features, on = ["node1", "node2"])
此函数执行一个查询,该查询从提供的数据帧中提取每对节点,并计算:
- 共同邻居(cn)
- 优先附件(pa),以及
- 邻居总数(tn)
对于每一对。这些措施在第一篇文章中被定义为。
我们可以将它应用到我们的训练和测试数据帧中,如下所示:
training_df = apply_graphy_features(training_df, "CO_AUTHOR_EARLY")
test_df = apply_graphy_features(test_df, "CO_AUTHOR")
对于训练数据帧,我们仅基于早期的图表计算这些指标,而对于测试数据帧,我们将跨整个图表计算它们。
我们仍然可以使用整个图表来计算这些特征,因为图表的演变取决于它在整个时间内的样子,而不仅仅是基于 2006 年及以后发生的事情。
Sample of the training DataFrame
现在我们准备训练我们的模型。我们可以用下面的代码做到这一点:
columns = ["cn", "pa", "tn"]X = training_df[columns]
y = training_df["label"]
classifier.fit(X, y)
我们的模型现在已经训练好了,但是我们需要评估它。
评估我们的模型
我们要计算它的准确度、精确度和召回率。下图摘自奥莱利图算法书,解释了这些指标是如何计算的。
Accuracy measures
scikit-learn 内置了我们可以用于此的函数。我们还可以返回模型中使用的每个特性的重要性。
以下函数将对此有所帮助:
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import accuracy_scoredef evaluate_model(predictions, actual):
accuracy = accuracy_score(actual, predictions)
precision = precision_score(actual, predictions)
recall = recall_score(actual, predictions)
metrics = ["accuracy", "precision", "recall"]
values = [accuracy, precision, recall]
return pd.DataFrame(data={'metric': metrics, 'value': values})def feature_importance(columns, classifier):
features = list(zip(columns, classifier.feature_importances_))
sorted_features = sorted(features, key = lambda x: x[1]*-1)
keys = [value[0] for value in sorted_features]
values = [value[1] for value in sorted_features]
return pd.DataFrame(data={'feature': keys, 'value': values})
我们可以通过运行以下代码来评估我们的模型:
predictions = classifier.predict(test_df[columns])
y_test = test_df["label"]evaluate_model(predictions, y_test)
Accuracy, Precision, Recall
我们在各方面都有很高的分数。现在,我们可以运行以下代码来查看哪个功能发挥了最重要的作用:
feature_importance(columns, classifier)
Feature Importance
我们可以在上面看到,公共邻居(cn)是我们模型中的主要特征。Common neighbors 向我们返回了一个作者拥有的未闭合合著三角形的数量,因此这可能并不令人惊讶。
现在,我们将添加一些由图形算法生成的新功能。
三角形和聚集系数
我们将从在测试和训练子图上运行三角形计数算法开始。该算法返回每个节点形成的三角形数量,以及每个节点的聚类系数。节点的聚类系数表示其邻居也是相连的可能性。
我们可以在 Neo4j 浏览器中运行以下 Cypher 查询,以在我们的列车图上运行该算法:
CALL algo.triangleCount('Author', 'CO_AUTHOR_EARLY', {
write:true,
writeProperty:'trianglesTrain',
clusteringCoefficientProperty:'coefficientTrain'});
和下面的 Cypher 查询来在测试图上运行它:
CALL algo.triangleCount('Author', 'CO_AUTHOR', {
write:true,
writeProperty:'trianglesTest',
clusteringCoefficientProperty:'coefficientTest'});
我们现在在节点上有 4 个新属性: trianglesTrain 、系数 Train 、 trianglesTest 和系数 Test 。现在,让我们在以下函数的帮助下,将这些添加到我们的训练和测试数据帧中:
def apply_triangles_features(data,triangles_prop,coefficient_prop):
query = """
UNWIND $pairs AS pair
MATCH (p1) WHERE id(p1) = pair.node1
MATCH (p2) WHERE id(p2) = pair.node2
RETURN pair.node1 AS node1,
pair.node2 AS node2,
apoc.coll.min([p1[$triangles], p2[$triangles]]) AS minTriangles,
apoc.coll.max([p1[$triangles], p2[$triangles]]) AS maxTriangles,
apoc.coll.min([p1[$coefficient], p2[$coefficient]]) AS minCoeff,
apoc.coll.max([p1[$coefficient], p2[$coefficient]]) AS maxCoeff
"""
pairs = [{"node1": pair[0], "node2": pair[1]}
for pair in data[["node1", "node2"]].values.tolist()] params = {"pairs": pairs,
"triangles": triangles_prop,
"coefficient": coefficient_prop}
features = graph.run(query, params).to_data_frame()
return pd.merge(data, features, on = ["node1", "node2"])
这些度量与我们迄今为止使用的不同,因为它们不是基于节点对计算的,而是特定于节点的度量。
我们不能简单地将这些值作为 node1Triangles 或 node1Coeff 添加到我们的数据帧中,因为我们不能保证节点对中节点的顺序。我们需要想出一个不可知论的方法
我们可以通过取值的平均值、值的乘积,或者通过计算最小值和最大值来实现这一点,就像我们在这里所做的那样。
我们可以将该函数应用于数据帧,如下所示:
training_df = apply_triangles_features(training_df,
"trianglesTrain", "coefficientTrain")
test_df = apply_triangles_features(test_df,
"trianglesTest", "coefficientTest")
现在我们可以训练和评估:
columns = [
"cn", "pa", "tn",
"minTriangles", "maxTriangles", "minCoeff", "maxCoeff"
]X = training_df[columns]
y = training_df["label"]
classifier.fit(X, y)predictions = classifier.predict(test_df[columns])
y_test = test_df["label"]display(evaluate_model(predictions, y_test))
Accuracy, Precision, Recall
这些功能非常有用!我们的每项指标都比最初的模型提高了大约 4%。哪些功能最重要?
display(feature_importance(columns, classifier))
Feature Importance
公共邻域仍然是最有影响力的,但三角形功能也增加了一些价值。
概括起来
这篇文章已经比我预期的时间长了很多,所以我们就到此为止,但是肯定还有一些练习留给读者。
工程更多功能
你能想到我们可以添加的任何其他功能来帮助我们创建一个更高精度的模型吗?也许其他社区检测,甚至中心算法可能会有所帮助?
扩展对链接预测的支持
目前,图算法库中的链接预测算法仅适用于单部图。单部图是指两个节点的标号相同的图。这些算法基于节点的拓扑结构,如果我们试图将它们应用于具有不同标签的节点,这些节点将可能具有不同的拓扑结构,这意味着这些算法不会工作得很好。
我们正在考虑添加适用于其他图形的链接预测算法版本。如果你有什么特别想看的,请在 GitHub 问题上告诉我们。
接下来呢?
如果你觉得这篇博文很有趣,你可能会喜欢我和艾米·霍德勒在过去 9 个月里写的《奥莱利图算法》一书。我们正处于最后的审查阶段,它应该在未来几周内可用。
您可以注册从 Neo4j 网站获得免费的电子版,网址:neo4j.com/graph-algorithms-book
威尔·里昂和我还在与 Neo4j 一起开发一个新的关于数据科学的在线培训课程,请关注在线培训页面,了解详细信息。
使用 MQTT 将 Python 应用程序与您的 Android 设备链接起来
跨平台通信和 MQTT 协议
在计算机科学中,两个不同系统之间的数据共享或通信是很常见的事情。开发系统时,可以设计两个同构系统之间的通信。此外,完全不同的系统可能也需要共享信息。例如,一个由 Arduino 平台驱动的简单传感器可能需要向运行 Android 或苹果 iOS 的智能手机发送信息。为了实现跨平台通信,可以声明和使用一组通用的通信规则。这样的规则集被称为通信协议。在这篇文章中,让我们来看看一个简单的跨平台通信协议;MQTT。
MQTT 代表 MQ 遥测传输。它是一个简单的消息协议,是为低带宽设备设计的。本质上,MQTT 被设计成一个物联网消息传递协议。如果你有兴趣了解更多关于 MQTT 的信息,请随时访问他们的官方网站和 FAQ 页面。
可用的 MQTT 服务
正如开头提到的,MQTT 是一种通信协议。MQTT 协议由几个服务提供者作为服务来实现和提供。有免费的、付费的和有限的 MQTT 服务提供者。访问这个 DIY 项目页面,看看一些 MQTT 服务的本质。
在本文中,我们将使用一个名为 CloudMQTT 的 MQTT 服务提供者。CloudMQTT 中有不同的付费计划和一个免费计划。免费计划是一项非常受限制的服务。不过出于教育和兴趣目的,还是用 CloudMQTT 的免费计划‘可爱的猫’吧。
在 CloudMQTT 设置免费数据通道
首先,进入https://www.cloudmqtt.com/并登录。您可以使用您的 google 帐户或 GitHub 帐户进行身份验证。然后,您必须创建一个新的实例。实例是托管在云上的消息代理对象。
提供实例的名称。添加一些标签。现在,选择免费的“可爱的猫”计划。
选择数据中心。请注意,您可能需要更改数据中心,并检查它是否正常工作。这是地区原因。根据您所在的区域,您将拥有一个正常工作的数据中心。
选择数据中心后,将创建您的实例。将会看到以下用户界面。在那里,您可以访问实例、设置、web-socket UI、活动连接和一些其他功能和信息的一般详细信息。
web-socket UI 选项卡使您能够发送和接收关于主题的消息。主题是实例内部的消息传递通道。一个实例中可以有多个主题。注意,为了在 web-socket UI 上发送关于特定主题的消息,您不必预先创建主题。只需将主题和消息指定为字符串并发送。该消息将被接收并显示在已接收消息表中。下图显示了在“测试/测试”主题上发送字符串“这是一条消息”后的结果。
创建 Android 应用程序
让我们讨论一下在云上创建的 MQTT 实例如何服务于我们的跨平台通信需求。为了演示,让我们创建一个 Android 应用程序,它通过 CloudMQTT 实例与一个简单的 python 程序通信。
在本帖中,我们将使用 Android Studio 。Gradle 将被用作构建工具。
在 Android studio 中创建新的 Android 项目。然后将下面的依赖项添加到build.gradle
(项目)文件中。该依赖项将添加一个 MQTT 客户端产品,它能够处理 MQTT 消息传递,同时您可以专注于您的应用程序及其业务模型。
repositories {
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
另外,将以下依赖项添加到build.gradle
(App)文件中。
dependencies {
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
}
执行 Gradle Sync,您就可以在您的 android 项目中使用 MQTT 客户端了。
你的应用需要以下权限。您可以通过向AndroidManifest.xml
文件添加以下权限来提供权限。确保在application
标签开始之前添加以下代码行(在application
标签之外)。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
另外,注册由导入的 MQTT 客户机提供的 MQTT 服务。在关闭application
标签之前添加下面一行代码(在application
标签内)。
<service android:name="org.eclipse.paho.android.service.MqttService" />
现在我们已经准备好编写代码来连接 MQTT 实例。让我们通过创建下面的MQTTHelper.java
类来简化我们的生活。注意,我已经将这个类包含在包helpers
中,你可以根据需要在另一个包中创建你的MQTTHelper
类。
重要提示:将端口
11111
更改为实例详细信息中指定的端口号。此外,相应地更改用户名和密码。您可以设置任何客户端 ID 和主题。
题目已经定为sensor/+
。这表示任何名称为“sensor/”后跟任何字符串的主题正在被监听。比如sensor/temperature
、sensor/humidity
、………所有的都被监听了。
在您的一个活动中创建一个TextView
元素。我在我的MainActivity
中创建了dataReceived
元素。那么活动:MainActivity
的 java 代码如下。
每当一条消息到达指定的主题时,就会显示在指定的TextView
元素上。让我们快速测试一下。转到 web-socket UI,在sensor/<something>
下发送任何消息。你应该可以在手机应用程序上看到你刚刚发送的消息。请注意,您必须将消息发送到您的移动应用程序设置为要收听的主题之一。在这篇文章中,是以sensor/
开头的任何话题。web-socket UI 将如下所示。
您可以通过访问 Connections UI 来验证您与 MQTT 实例的连接。下图显示了 MQTT 与移动应用程序的连接。
写一个简单的 Python 程序
现在,您的移动应用程序上有了一个工作的 MQTT 客户机,您希望监听由另一个平台发送的消息。在这篇文章中,让我们用一个简单的 python 程序来连接移动应用程序。即使我们说 python 程序要与移动应用程序对话,它也不是直接通信。消息将通过 CloudMQTT 消息代理发送。整体架构如下。
在编写我们的 python 程序之前,我们必须用 python 安装 paho MQTT 客户端库。注意,移动应用程序中使用了相同的 MQTT 客户端。使用 pip 安装相应的库。
pip install paho-mqtt
python 程序非常简单。我就把代码留在下面吧。
import paho.mqtt.client as mqtt
mqttc = mqtt.Client("client1", clean_session=False)
mqttc.username_pw_set("#User", "#password")
mqttc.connect("#Server", #port, 60)# Set a stopping condition or just press ctrl+C to quit.
while(True):
inp = input()
mqttc.publish("sensor/temp", payload=inp, qos=0)
确保在 python 脚本中指定所有 MQTT 实例细节。此外,可以在 python 程序中的任何地方随意使用这个简单的 MQTT 客户机publish
方法调用。请注意,您发布的消息将在 web-socket UI 和移动应用程序上可见。
我还能连接什么?
消息代理并不局限于 Python 和 Android 之间。您可以用几个可用的 MQTT 客户端库连接其他平台。在文档中,点击您平台上偏好的语言,找到最合适最稳定的客户端。目前有 Ruby,Python,Node.js,Java,Go,。NET,NodeMCU 和 PHP 客户端正式可用。做一点研究,你可能会找到其他语言的客户。
希望你喜欢并利用了这篇文章。android 应用程序的代码可以在这个 GitHub 仓库中找到。
链表实现指南
介绍
在本指南中,我将解释如何实现一个链表。如果你不知道什么是链表,我推荐你阅读我的另一个博客,在这里找到,在那里我详细解释了什么是链表以及它是如何工作的。对于这个博客,我将重点讨论实现。
实施指南
下图演示了链表的实现。虽然该演示是用 C 语言编写的,但是所示的过程和步骤适用于使用任何编程语言实现链表。
必需的库和结构定义
下面的图 1 包括所需的库,定义了在整个演示过程中常用的两个函数,并详细说明了节点和链表的结构。节点被定义为包含一个整数(数据)和一个指向下一个节点的指针。同样,一个列表包含一个表示列表大小的整数和一个指向列表中第一个节点的指针(head)。函数 initList 将头指针初始化为空,并将列表的大小初始化为零,因为最初列表中没有节点。函数 isEmpty 确定一个列表是否为空。
Figure 1: Require libraries and structure/function definitions
将节点添加到列表中
insertFront 函数接受两个参数:一个链表和一个要插入到列表中的值。图 2 中的第 30 行为一个节点分配内存,并初始化一个指针指向分配的空间。如果成功分配了内存,该函数会将列表的大小增加 1,并在将新节点插入列表之前确定列表是否为空。如果列表为空,列表的头指针指向新节点,新节点的下一个指针指向 null。否则,新节点的 next 指针指向链表头所指向的节点,然后链表头指针更新指向新节点。
Figure 2: Function to insert node to the front of a linked list
将节点追加到列表
函数接受两个参数:一个链表和一个插入到列表末尾的值。图 3 中的第 49 行为一个节点分配内存,并初始化一个指针指向分配的空间。如果成功分配了内存,该函数会将列表的大小增加 1,并在将新节点插入列表之前确定列表是否为空。如果列表为空,则通过更新列表的头指针指向新节点,将新节点插入到列表中。否则,初始化指针以遍历列表。指针遍历列表,直到指向最后一个节点。一旦找到最后一个节点,该节点的 next 指针就会更新,指向新节点。
Figure 3: Function to insert node to the end of a linked list
从列表前面删除节点
函数 deleteFront 接受一个参数:一个链表。该函数首先确定列表是否为空。如果列表不为空,那么函数初始化一个指针指向列表中的第一个节点。然后更新列表的头指针,指向列表中的下一个节点,如果列表中没有其他节点,则为 null。然后,使用之前初始化的指针从内存中移除第一个节点。最后,列表的大小减一。
Figure 4: Function to remove first node from linked list
从列表末尾删除节点
deleteEnd 函数接受一个参数:一个链表。该函数首先确定列表是否为空。如果不为空,则初始化两个指针。一个指针(currentNode)指向第一个节点,而另一个指针(nextNode)指向下一个节点,或者如果列表中只有一个节点,则为 null。nextNode 指针用于遍历链表,直到指向最后一个节点,currentNode 指针指向倒数第二个节点。然后,currentNode 的 next 指针被更新为 null,nextNode 的指针用于从内存中删除最后一个节点。最后,列表的大小减一。
Figure 5: Function to remove last node from linked list
在所需位置插入节点的函数
insertNodeAtLocation 函数接受三个参数:一个链表、一个位置和值。该函数的目的是在所需位置插入一个节点。例如,如果所需位置是 3,数据值是 5(insertNodeAtLocation(list,3,5)),那么函数将创建一个数据值为 5 的节点,并将创建的节点作为列表中的第三个节点。为了满足函数的要求,第一步是通过检查输入位置的值是否等于或大于 1 但小于列表大小加 1 来确定输入位置是否有效。如果位置有效,该函数将检查所需位置的值是否为 1,然后调用图 2 所示的 insertFront 函数。否则,列表的大小增加 1,创建一个计数器来跟踪列表中的当前位置,为新节点分配内存,并初始化一个指针以遍历列表。一旦指针指向其位置正好在期望位置之前的节点(节点 A ),新节点的 next 指针被更新以指向当前跟随节点 A 的节点。然后,节点 A 的 next 指针被更新以指向新节点。
Figure 6: Function to insert a node at a desired location in the linked list
用于删除具有指定值的节点的函数
函数接受两个参数:一个列表和一个值。该函数首先确定列表是否为空。如果不为空,该函数将初始化两个指针(currentNode 和 prevNode)。最初,currentNode 指向列表的头部,而 prevNode 指向 null。然后 removeNode 函数检查当前节点的数据是否等于输入值。如果它们相等,则调用图 4 所示的 deleteFront 函数。否则,prevNode 指针更新为指向 currentNode,currentNode 更新为指向下一个节点。然后,指针遍历列表,直到 currentNode 等于 null。如果在迭代 currentNode 的数据的任何一点等于输入的值,列表的大小减少一,prevNode 的下一个指针更新为指向 currentNode 后面的节点,currentNode 用于在函数终止之前从内存中删除已删除的节点。如果输入的值在列表中找不到,该函数会向控制台输出“列表中找不到值”。
Figure 7: Function to remove first node in list containing specified value
打印链接列表
函数接受一个参数:一个链表。该函数首先初始化一个指针,该指针将用于遍历列表。如果列表为空,函数将向控制台输出“列表为空”。否则,这些函数输出列表中的节点数,以及列表中每个节点的数据值。
Figure 8: Function to print the value of each node in the list
主要功能
**主函数是执行 C 程序时运行的函数。如图 9 所示,使用上面定义的函数初始化链表,并从列表中插入/删除节点。
Figure 9: The main function
执行时,main 函数输出如下内容。
Figure 10: The output from the main function
结论
总之,链表是一种非常酷的结构化数据的方式。如前所述,如果你想了解链表如何工作或何时使用它们,请查看我的另一个博客这里 。
链表与数组
介绍
有许多不同类型的数据结构可以在计算机程序中实现,例如数组和链表。每种类型的数据结构都有其优点和缺点。由于这些原因,在设计、优化和扩展程序时,理解不同类型的数据结构的优缺点是很重要的。
数组
让我们从定义一个数组开始。简而言之,数组本质上是一种数据结构,类似于相同数据类型的值列表。数组的另一个特性是数组元素存储在连续的内存位置。此外,数组具有固定的大小,这是在初始化时定义的。根据这些属性,您能看出数组的优点和缺点吗?
阵列的优势
搜索时间:
如前所述,数组将元素存储在连续的内存位置。这意味着可以通过向数组的基值或第一个元素的位置添加偏移量来访问任何元素。下面的数字更详细地说明了我的意思。
Figure 1: Demo program to illustrate consecutive memory locations
Figure 2: Output of program in Figure 1
虽然图 2 中的内存地址看起来相隔四个,但实际上它们是连续的。这意味着,只要通过向第一个元素的地址添加偏移量来知道元素的索引,就可以定位数组中的任何元素。由于搜索数组中的第二个或最后一个元素没有时间差,所以数组具有恒定的搜索次数或大 O(O(1)),这非常快。基本上,最好的运行时间复杂度是 O(1)。
数组的缺点
浪费的内存:
数组的缺点之一是内存可能会被浪费。为了解释这一点,我将描述一个场景。作为一名程序员,你并不总是知道分配多少内存。例如,您正在构建一个应用程序,它将要求用户输入内容,然后将这些内容存储在一个数组中。因为您不知道用户将输入多少,所以您用一百万个索引初始化一个数组,因为您认为一百万个输入对任何用户来说都足够了。如果用户只向数组中输入十万个元素会怎样?然后,90%的分配空间被浪费。
缓慢的插入/删除时间:
数组的插入和删除时间很慢。让我们从关注插入开始。要将元素插入到数组的前面或中间,第一步是确保数组中有空间容纳新元素,否则需要调整数组的大小。下一步是通过将每个元素移动到所需的索引之后来为新元素留出空间。同样,对于删除,在删除一个元素后需要进行移位。这意味着数组的插入时间是 n 的大 O(O(n)),因为 n 个元素必须移位。然而,从数组末尾插入和删除是 O(1)。
链接列表
链表是收集类似数据的另一种方法。然而,与数组不同,链表中的元素不在连续的内存位置。链表由使用指针相互连接的节点组成。下面的图 3 展示了一个链表。
Figure 3: Diagram of singly linked list structure
如图 3 所示,单链表由一个头和一组节点组成。请注意,有许多类型的链表,如单向链表和双向链表,但现在我们将集中讨论单向链表。为了解释单链表是如何工作的,我必须首先定义一个指针。指针是保存另一个变量或结构地址的变量。在图 3 中,head 是一个指针,它包含链表上第一个节点的地址。head 变量允许计算机定位内存中的第一个节点并访问其数据。一旦定位了起始位置(第一个节点),就很容易遍历链表,因为每个节点都包含一个指向下一个节点的指针。
链表的优点
更好地利用内存:
从内存分配的角度来看,链表比数组更有效。与数组不同,链表的大小不是预先定义的,允许链表在程序运行时增加或减少大小。这是可能的,因为要插入或删除链表,指针需要相应地更新。
通常,在链表中插入一个节点需要在新节点初始化后更新指针。图 4-6 描述了如何在链表的开头、中间或结尾插入节点。
Figure 4: Diagram of inserting a node to the beginning of a linked list
Figure 5: Diagram of inserting a node in the middle of a linked list
Figure 6: Diagram of appending a node to a linked list
从链表中删除比插入更容易。唯一的步骤是更新指针。图 7-9 展示了删除链表开头、中间或结尾节点的过程。
Figure 7: Diagram of deleting first node from linked list
Figure 8: Diagram of deleting node from the middle of linked list
Figure 9: Diagram of deleting last node from linked list
如上图所示,在链表中插入和删除非常简单。此外,链表结构防止了内存浪费,因为节点是在初始化时插入到链表中的。
快速插入/删除时间:
如图 4 所示,在链表的开头或结尾插入一个新节点需要花费恒定的时间(O(1)),因为唯一的步骤是初始化一个新节点,然后更新指针。同样,如果有一个尾指针(类似于头指针),插入到链表的末尾也将是 O(1)。然而,插入到链表的中间需要线性时间(O(n)),因为在插入节点之前需要迭代 n 个元素才能到达正确的位置。类似地,删除链表开头和结尾的节点需要恒定的时间,而删除链表中间的节点需要线性时间。
链表的缺点
较慢的搜索时间:
链表的搜索时间比数组慢,因为不允许随机访问。与可以通过索引搜索元素的数组不同,链表需要迭代。这意味着,如果您想要获得第十个节点上的数据,可以使用 head 指针来获得第一个节点,可以使用第一个节点上的指针来获得第二个节点,依此类推,直到到达第十个节点。这意味着在搜索一个节点时必须迭代的节点越多,搜索时间就越长。这意味着链表的搜索时间是线性时间或 n 的大 O(O(n))。
结论
总之,有许多不同的数据结构。每种数据结构都有优缺点,根据任务的不同会影响性能。今天,我们探讨了两种数据结构:数组和链表。数组允许随机访问,每个元素需要的内存较少(不需要指针空间),但插入/删除操作和内存分配效率较低。相反,链表是动态的,具有更快的插入/删除时间复杂度。然而,链表具有较慢的搜索时间,并且指针需要列表中每个元素的额外内存。下面的图 10 总结了数组和链表的优缺点。
如果你有兴趣学习如何实现链表,可以看看我的链表指南 这里 。
Figure 10: Summary of strengths and weaknesses of arrays and linked list
Linode 可能是最好的部署解决方案
Christopher Aker 创建的虚拟私有服务器托管服务是我使用过的最强大、可扩展的远程在线托管解决方案之一。对于我们这些在地下室没有数据中心的人来说,Linode 是一种高可靠性和高速度的网络主机的理想方式。对于 Flask、Node、React、Django 后端来说,Linode 绝对是一个很棒的选择。
可攀登的
有了 Linode,你可以随时调整你的私人服务器的大小。从每月 10 美元开始,到 960 美元结束,Linode 的计算能力相当强大。
Linode 还允许您获得更多 RAM 或专用 CPU,以及专用显卡。GPU 通常是非常高端的 RTX6000 系列卡。由于一个计划中有多达四个选项,很容易理解为什么从 Linode 开始会容易得多。
多才多艺的
像任何虚拟私人服务器一样,Linode 的优势在于它是一台成熟的计算机,而不是像共享主机那样的东西,它给你一个存储位置和一些共享内存。许多共享选项也很难修改,有时还会受到限制。
使用 Linode,您可以获得自己的远程无头 Linux 机器的能力,它有自己的转发端口和 IP。不用说,这使得它更加通用。通常,虚拟专用服务器用于更好的服务器管理,以及为基于 web 的应用程序运行后端计算。
不使用“全栈”服务、API 和语言的 Web 开发工作正在慢慢淡出,静态网站正在成为过去,特别是对于数据驱动的应用程序。
图像管理
Linode 带来的一个同样令人兴奋的概念是简单管理。服务器非常容易从映像中立即重建。等待 5-10 分钟来安装你的 RedHat 新版本,然后你就可以开始运行了!
想换成 Debian 服务器?这很好,只需从列表中选择图像并将其写入服务器。我最近在我的一个笔记本上重写了我的有问题的红帽系统,并被它的简单所吸引。
专有技术
虽然 Linode 确实很棒,但它肯定不适合所有人。如果你不擅长 Bash,不知道如何使用 nano 和 nsgiw,我不建议你使用 Linode 而不是更简单的东西。
奖励:纳米节点
另一个有趣的概念是“纳米电极”一个 nanode 是一个完整的 Linode 的较小版本,资源最少,每月 5 美元。但是考虑到这一点,对于 web 请求,我认为 Nanodes 肯定非常适合管道部署!
只要你知道如何建立你的新网站,并拥有一些基本的开发操作技能,利用 Linode 肯定可以让你的钱包里有更多的钱,更多的计算可以远程进行。虽然 AWS 和 Google Cloud 是很好的计算平台,但是说实话,它们很贵。如果你正在寻找一台服务器来托管你的作品集,我当然会选择 Linode。
数据科学内容的主要来源列表
我最喜欢的数据科学内容来源的非详尽列表,请欣赏!
在本文中,我将源代码分为三个“不同”的类别:
- 播客
- 博客
- 通用
请享用。希望你觉得有用。确保 跟随我的简介 🔍如果你喜欢这篇文章,并想看到更多!
播客
在通勤或放松时,播客是了解数据科学相关新闻和突破的好方法。播客的范围从高度事实和教育到更放松和假设。
我会在每个条目前面加上所有者自己的“关于”段落。
数据帧— 由 DataCamp 提供
数据科学是发展最快的行业之一,被称为 21 世纪最性感的工作。但是数据科学到底是什么?在 DataCamp 的播客中,Hugo Bowne-Anderson 从数据科学试图解决什么问题而不是什么定义最适合它的角度来处理这个问题。从自动医疗诊断和自动驾驶汽车到推荐系统和气候变化,与行业和学术专家一起探索将影响 21 世纪的行业内部工作方式。
播客
Podcastwww.datacamp.com 数据营](https://www.datacamp.com/community/podcast)
不那么标准偏差——罗杰·彭&希拉里·帕克
不那么标准差:数据科学播客 Roger Peng 和 Hilary Parker 谈论学术界和工业界的数据科学和数据分析的最新进展。共同主持人:约翰霍普金斯大学彭博公共卫生学院的 Roger Peng 和 Stitch Fix 的 Hilary Parker。
不那么标准差:数据科学播客罗杰·彭和希拉里·帕克谈论数据科学的最新进展…
nssdeviations.com](http://nssdeviations.com/)
数据怀疑论者——凯尔·波利奇
数据怀疑论者制作了这个网站和两个播客。该节目由凯尔·波利奇主持。Linh Da Tran 共同主持我们的迷你剧集。
我们的主要产出是每周播客,其中包括解释数据科学高级概念的小短片,以及对研究人员和从业人员的较长采访片段。
奖金饲料是额外的和扩展的材料,如果你只是不能得到足够的数据怀疑论者。
数据怀疑论者是你对统计学、机器学习、大…
dataskeptic.com](https://dataskeptic.com/)
博客
和播客一样,有些博客纯粹是教育和辅导性质的,有些则更像轶事。所有这些都是强烈推荐的!
简单统计——拉法·伊里扎里、罗杰·彭&杰夫·莱克
关于这个博客:我们将发布我们感兴趣的想法,为科学/流行写作的讨论做出贡献,链接到激励我们的文章,并与未来的统计学家分享建议。
为什么是“简单统计”:我们需要一个标题。此外,我们喜欢用简单的统计数据来解决真正重要的问题。我们不喜欢不必要的复杂——那只会导致谎言,该死的谎言和其他东西。
编者按:有一段时间,我们对统计学家和数据科学家进行了一系列采访,但事情变得…
simplystatistics.org](https://simplystatistics.org/)
统计建模——安德鲁·盖尔曼
关于这一点,没有官方的介绍,但它来自哥伦比亚大学的教授安德鲁·盖尔曼。
有一天,有人向我推荐了詹姆斯·考夫曼和弗拉德·格列弗努在一本心理学杂志上发表的一篇文章…
statmodeling.stat.columbia.edu](https://statmodeling.stat.columbia.edu/)
R 博客作者——由多位作者撰写
R-Bloggers 是关于授权 blogger 去授权其他 R 用户。
它是一个博客聚合器,内容由写 R(英文)的博客作者提供。该网站帮助 R 博客作者和用户联系并关注“R 博客圈”(你可以观看 useR2011 的 7 分钟演讲,了解更多关于 R 博客圈的信息)。
在数据中发现一个有趣的模式总是令人兴奋的,它似乎指向一些重要的差异或…
www.r-bloggers.com](https://www.r-bloggers.com/)
机器学习大师——杰森·布朗利
没有具体的关于这一页,但贾森布朗利是一个绝对的传奇。他每天都写作,并在他的博客上产生了大量高质量的内容。
我最常被问到的问题是:“我该如何开始?”我对获得…的最佳建议
machinelearningmastery.com](https://machinelearningmastery.com/start-here/)
流动数据 —作者 Nathan Yau
FlowingData 探索统计学家、设计师、数据科学家和其他人如何使用分析、可视化和探索来理解数据和我们自己。
人数上的优势
flowingdata.com](https://flowingdata.com/)
可能想多了——艾伦·唐尼
艾伦·唐尼是奥林学院的教授,也是《思考巨蟒》(Think Python)和《思考贝叶斯》(Think Bayes)的作者,其他书籍可从绿茶出版社(T21)获得。
[## 可能想多了
艾伦·唐尼的博客
www.allendowney.com](https://www.allendowney.com/blog/)
直观解释—由 Setosa 提供
直观解释(EV)是一个让难以理解的想法变得直观的实验,Bret Victor 的探索性解释启发了他的工作。
[## 直观解释
直观解释(EV)是一项实验,旨在让难以理解的想法变得直观
setosa.io](http://setosa.io/ev/)
一般
这最后几个在这里只是因为他们并不真正适合其他类别,虽然没有很多!
看见理论 —丹尼尔·库宁
丹尼尔·库宁在布朗大学读本科时创立了视觉理论。该网站的目标是通过交互式可视化使统计数据更容易获取。
概率和统计的直观介绍。
seeing-theory.brown.edu](https://seeing-theory.brown.edu/)
走向数据科学 —数据科学及更多内容的出版商
我认为这个问题不需要太多解释😉
分享概念、想法和代码。
towardsdatascience.com](https://towardsdatascience.com)
结束语
我希望这篇短文对你有所帮助!
阅读彼得·尼斯特拉普在媒介上的作品。数据科学、统计和人工智能…推特:@PeterNistrup,LinkedIn…
medium.com](https://medium.com/@peter.nistrup)
听听我们的叙述文章
在您的时间表上方便地收听我们的一些最佳内容
我们的使命是为每个人提供最好的内容。我们热衷于以最适合你的方式让你接触到创新的概念和想法。从我们的播客到我们的时事通讯到我们的出版物本身,我们一直在努力以各种方式与你们分享前沿概念。
我们现在正致力于通过提供一些我们最好的文章的便捷音频解说来让您的生活更加美好。
我们选择的叙述性文章包括由我们的专业叙述者团队精心策划的内容。听听迈克尔·邦纳、杰纳·康纳利、埃里克·麦克法登和爱丽丝·伊里扎里在你度过每一天时为你带来的这些惊人的概念。
方便的时候听听,了解一些数据科学、机器学习、人工智能、编程等领域最不可思议的想法。
示例:
https://towardsdatascience.com/narrated/home
我们也让我们的作者提出他们自己的录音,就像丹尼尔在这里做的一样。将您的完整音频以 mp3 格式发送到 publication@towardsdatascience.com。
如果您对我们如何改进本部分有任何想法,请随时发送您的反馈。
谢谢!
Lit BERT: NLP 迁移学习的三个步骤
BERT (Devlin 等人,2018)可能是最流行的迁移学习的 NLP 方法。由 Huggingface 实现的这个 API 提供了很多不错的特性,并抽象出了漂亮 API 背后的细节。
PyTorch Lightning 是一个轻量级框架(实际上更像是重构你的 PyTorch 代码),它允许任何使用 PyTorch 的人,如学生、研究人员和制作团队,轻松扩展深度学习代码,同时使其可复制。它还通过教练标志提供 42+高级研究功能。
Lightning 没有在 PyTorch 上添加抽象,这意味着它可以很好地与其他伟大的软件包一起使用,比如 Huggingface!在本教程中,我们将使用他们的 BERT 实现在 Lightning 中执行微调任务。
在本教程中,我们将分三步进行 NLP 的迁移学习:
现场演示
如果您更愿意在实际代码中看到这一点,复制这个 colab 笔记本!
微调(又名迁移学习)
如果你是一名试图改进 NYU 胶水基准的研究人员,或者是一名试图理解产品评论以推荐新内容的数据科学家,你正在寻找一种方法来提取一段文本的表示,以便可以解决不同的任务。
对于迁移学习,通常有两个步骤。您使用数据集 X 来预训练您的模型。然后,您使用该预训练模型将该知识带入求解数据集 b。在这种情况下,BERT 已经在图书语料库和英语维基百科上接受了预训练【1】。下游任务是你关心的解决胶合任务或分类产品评论。
预训练的好处是,我们在下游任务中不需要太多数据就能获得惊人的结果。
用 PyTorch 闪电微调
一般来说,我们可以使用以下抽象方法对 PyTorch Lightning 进行微调:
对于迁移学习,我们在 lightning 模块中定义了两个核心部分。
- 预训练模型(即:特征提取器)
- finetune 模型。
您可以将预训练模型视为特征提取器。这可以让您以一种比布尔或一些表格映射更好的方式来表示对象或输入。
例如,如果您有一个文档集合,您可以通过预先训练的模型运行每个文档,并使用输出向量来相互比较文档。
微调模型可以任意复杂。它可能是一个深度网络,也可能是一个简单的线性模型或 SVM。
用 BERT 微调
Huggingface
在这里,我们将使用一个预训练的 BERT 来微调一个名为 MNLI 的任务。这实际上只是试图将文本分为三类。这是照明模块:
在这种情况下,我们使用 huggingface 库中预先训练的 BERT,并添加我们自己的简单线性分类器,将给定的文本输入分类到三个类别之一。
然而,我们仍然需要定义验证循环来计算我们的验证准确性
以及计算我们测试精度的测试回路
最后,我们定义将要操作的优化器和数据集。该数据集应该是您试图求解的下游数据集。
完整的 lightning 模块看起来像这样。
摘要
在这里,我们学习了在 LightningModule 中使用 Huggingface BERT 作为特征提取器。这种方法意味着您可以利用一个真正强大的文本表示来做如下事情:
- 情感分析
- 对聊天机器人的建议回复
- 使用 NLP 构建推荐引擎
- 改进谷歌搜索算法
- …
- 为文档创建嵌入以进行相似性搜索
- 任何你能创造性地想到的东西!
你也看到了 PyTorch Lightning 与包括 Huggingface 在内的其他库的配合有多好!
使用机器学习和谷歌地图实时预测交通事故风险
在这里,我描述了使用 scikit-learn、 Google Maps API、Dark Sky API、Flask 和 PythonAnywhere 创建和部署交互式交通事故预测器。
Source: unsplash
交通事故非常普遍。如果你像我一样生活在一个杂乱无章的大都市,你可能听说过、目睹过、甚至参与过其中。由于交通事故频繁发生,交通事故是全球死亡的主要原因,每年缩短数百万人的生命。因此,能够预测交通事故或事故易发区域的发生的系统能够潜在地挽救生命。
交通事故预测虽然困难,但并非不可能。事故不会以纯粹随机的方式出现;它们的发生受多种因素的影响,如驾驶员的身体状况、车型、驾驶速度、交通状况、道路结构和天气。研究历史事故记录将有助于我们理解这些因素和道路事故之间的(潜在因果)关系,这反过来将允许我们建立一个事故预测器。
幸运的是,有几个这样的事故记录是公开的!例如,英国政府公布了自 2002 年以来该国交通事故的详细记录。有了这个数据集,我和我的队友们已经创建并部署了一个 机器学习模型 ,它可以高精度地预测大伦敦地区何时何地可能发生事故。
在这篇文章中,我将带你完成这个项目的过程。完整的代码可以在我的 GitHub 库中找到,而实时模型可以在这个网站中访问。
[## 道路交通事故
在这个项目中,我们建立了一个机器学习模型,预测在不同地点的 RTAs 的概率…
kteo7.pythonanywhere.com](https://kteo7.pythonanywhere.com/)
目标
在开始这个项目之前,我们为自己设定了一个明确的目标:**我们希望创建一个任何人都可以轻松访问的交互式交通事故预测器。**我们认为实现这一目标的最佳方式是在网站上部署一个训练有素的预测器。这个预测网站应该能够做到以下几点:
- 允许用户输入出发地和目的地(两者都必须在大伦敦),并找到连接两者的最佳驾驶路线。
- 允许用户选择他们计划进行旅行的日期/时间,并确定在该时间窗口内沿线特别容易发生事故的区域。
下图描述了我们为实现这一目标而采取的步骤。我将在接下来的章节中详细阐述它们。
Steps in this project
数据收集
确定了目标后,我们开始寻找必要的数据。以下是我们最终选择的两个数据集:
卡格尔
我们在这个项目中使用的主要数据集是英国政府发布的详细事故记录,并在 Kaggle 上托管。他的数据集包含了 2000 年至 2014 年间发生在英国的 160 万起交通事故的细节。它由 33 列组成,记录了事故的地点、时间、严重程度以及各种气象和交通背景等细节。在这个项目中,我们仅限于分析 2012 年至 2014 年大伦敦地区的交通事故。
黑暗的天空
我们认为天气是道路事故中一个特别重要的因素。尽管前面提到的 Kaggle 数据集包含了气象信息,但我们认为这还不够。使用 Kaggle 数据集的weather_condition
列有两个问题:
- 它假设天气全天不变。如果你去过伦敦,你就会知道为什么这个假设是有问题的——那里的天气变化非常频繁!
- **它只包含历史天气记录(显然!).**为了使用
weather_condition
作为未来事故的预测器,我们需要一种方法来获得天气预报。
由于这些原因,我们决定转而利用美国一家专门从事天气预报的公司 【黑暗天空】 提供的气象数据。黑暗天空每半小时提供一次过去的记录和未来的天气预报,这完全符合我们的目的。
数据处理
DBSCAN 聚类
在研究数据集时,我们发现伦敦几乎每条街道都曾是事故现场。这是有道理的——伦敦是一个繁忙的城市,交通事故(包括小事故)经常发生。下图是使用 Tableau 创建的,在伦敦地图上叠加了事故地点(红点)。注意整个城市是如何沐浴在红色之中!
The city of London. Each red spot indicates an accident between 2012 and 2014. As we can see, London is full of (historical) car accidents!
尽管全市都发生过事故,但有些地区比其他地区更容易发生事故。为了系统地识别这些事故热点,我们将上述事故地点分组。我们将一个聚类定义为一个横截面为 25 米的区域,其中在两年时间窗口内至少发生了 14 起事故。
我们使用 DBSCAN 算法来执行这种聚类。**选择 DBSCAN 是因为它的速度、发现任意形状簇的能力以及对异常值的鲁棒性。**落在聚类之外的事故点被视为异常值,并从我们随后的分析中排除。
A depiction of how DBSCAN works
DBSCAN 聚类步骤发现了 473 个事故热点。下面是使用地理分析软件 ArcGIS 对这些热点进行的可视化处理。我们可以看到热点集中在伦敦的主要道路上,如 A11 和 A13。
Accident hot spots in London
负采样
此时,我们有数百个事故热点。我应该澄清一件事——这些热点并非 100%的时候都容易发生事故。例如,伦敦主干道沿线的事故多发点在高峰时段可能很危险,但在安静时段相对安全。另一方面,急转弯中的热点在阳光明媚的日子里可能是相当无害的,但在下雪或下雨的日子里可能会变得致命。
因此,我们的下一个任务是找出这些热点在什么条件下被“激活”。通过这种方式,我们的项目现在变成了一个经典的二元分类问题:我们需要找出能够“激活”热点的因素组合(例如天气、时间等)。
然而,我们此时的数据还不足以创建一个分类模型!训练分类器需要正样本和负样本,但我们只有正样本(即目标标签为 1 的记录)。因此,我们需要一种生成阴性样本(即“非事故”记录)的方法。
先前已经描述了使用阳性样品产生阴性样品。我们遵循袁等人 **所描述的方法,实质上,该方法需要为聚类中的每个阳性样本随机生成三个阴性样本。**例如,如果我们有一个位于 Abbey Road 的聚类,其中发生了 15 起事故,我们将随机合成该地点的 45 条非事故记录。除了它们的位置,这些阴性样本的其他预测特征,如非事故事件的日期和时间,是随机选择的。小心谨慎,以免这些随机合成的阴性样品与真正的阳性样品不一致。
探索和观察
在研究我们清理过的数据集时,我们发现了一些有趣的现象。一些更有趣的观察结果详述如下。
哪些月份最容易发生道路交通事故?
上面的热图显示了所有日历月中一周内每天发生的事故数量。我们可以看到,9 月至 12 月是事故发生频率相对较高的月份。这是伦敦秋季的几个月,其特点是低温、多雾和多雨。与直觉相反,寒冷的冬季一月和二月发生的事故较少。这可能表明人们更不愿意在这段时间开车。
一天中的什么时候最容易发生道路交通事故?
上面的热图显示了一周中每天每小时发生的交通事故数量。我们观察到,不出所料,大多数事故发生在早上 8 点到 9 点的早高峰时间和下午 3 点到 7 点的晚高峰时间(紫色块)。星期五尤其糟糕。
每起事故涉及多少辆车?
上面的条形图显示了基于涉及车辆数量的事故分布。它表明大多数事故涉及一辆或两辆汽车。这在直觉上是有意义的;大多数事故都是由于汽车偏离道路和/或两辆汽车相撞而发生的。
哪些区的交通事故发生率最高?
上面的条形图显示了伦敦不同行政区发生的事故数量。我们看到威斯敏斯特位居第一,泰晤士河畔的金斯敦交通事故最少,排在最后。下面的地图以不同的方式展示了同样的信息:它用较暗的紫色突出了容易发生事故的行政区。
监督学习
有了前面提到的预处理步骤,我们终于准备好进入建模阶段了!我们按照 70:30 的比例将数据集分为训练和测试数据集。
Python 的 scikit-learn 库用于模型训练和评估。我们尝试了常见的分类模型,如 SVM、逻辑回归和随机森林。使用准确度和受试者工作特征曲线下面积(AUC-ROC)来测量和比较模型的相对性能。下表总结了我们建模步骤的结果。
我们发现,在我们测试的模型中,表现最好的模型是仅根据数字特征训练的随机森林。这是我们为部署选择的模型,我将在下面描述这个过程。
部署
然后,我们使用 Python web 框架 Flask 将我们的 scikit-learn 模型打包成一个 web 应用程序。该网站的前端显示是基于一个取自网站 HTML5UP 的免费模板构建的。所有的 html、javascript 和 CSS 代码都集成到 Flask 应用程序中。
然后,PythonAnywhere(一个专注于 Python 的 web 托管服务)将该应用程序发布到网上。你可以在kteo7.pythonanywhere.com访问网站。以下是网站截图:
现在我想提供 Flask 应用程序工作的更多细节。
在前端,有字段,用户可以输入他们选择的起点和终点。这些文本字段配备了提供自动完成功能的 Google Places API。还有一个下拉菜单,用户可以选择日期/时间(可以是过去、现在或未来 48 小时内)。
一旦输入完成,一个POST
请求就会被发送到后端框架。这里,这些输入充当函数call_google
的参数。**给定出发地和目的地,该函数将调用 Google Maps API,返回连接两地的最佳行驶路线。**更具体地说,该函数返回路线的航路点的纬度和经度,这些航路点是沿路线有规律间隔的点。
纬度和经度作为另一个名为calc_distance.
的功能的输入。该功能从这些航路点“画”出半径为 50 米的假想圆,并检查是否有任何事故热点落在这些航路点内。
对于落入圆圈内的每个星团,另一个函数call_darksky
向黑暗天空 API 发出请求。该函数将返回指定时间内该地点的天气预报。
利用天气数据以及日期/时间信息,**最终预测功能将对感兴趣的热点在所选天气/时间下是否被“激活”进行二元预测。**这些预测然后被发送到前端,前端将在屏幕上显示它们。
下面的 gif 展示了我们的交互模型。在这里,我选择了一个未来的日期和时间(10 月 12 日星期六下午 6 点),选择了一个起点(海德公园)和一个终点(国王十字车站)。我们可以看到,该模型选择了一条穿过 Gloucester Place 和 A501 的路线,并将这两条道路之间的繁忙交叉路口标记为潜在的事故地点。
同样,你可以在kteo7.pythonanywhere.com访问这个模型。我鼓励你在它仍然活跃的时候(它将一直活跃到 2020 年初)尝试一下。
注:除非另有说明,以上所有图片和动画均属本人所有。
如果你对这篇文章有任何意见或者想联系我,请随时通过 LinkedIn 给我发一个联系方式。另外,如果你能通过我的推荐链接成为一名中级会员来支持我,我将非常感激。作为一名会员,你可以阅读我所有关于数据科学和个人发展的文章,并可以完全访问所有媒体上的故事。
这个项目是“数据和可视化分析”课程的一部分,我在佐治亚理工学院攻读分析硕士学位时选修了该课程。特别感谢我在这个项目中的队友——katan nya,Roger,Siew Lee 和 Yeok!你们太棒了。
使用 Tweepy、Keras 和 Django 对 Twitter 数据进行实时情感分析
Photo by Marten Bjork on Unsplash
欢迎来到这篇关于在推特上进行实时情绪分析的教程。我相信你一定遇到过复杂的仪表板,上面有大量的图表和数字正在被处理,看起来就像科幻电影一样,令人敬畏。这就是我们的目标。
Some Complex looking dashboard
虽然我们的最终结果不会像这样广泛,但是,它会教你如何进行必要的数据连接,这样你就可以使它像你想要的那样复杂。你可以继续把它作为一个 SaaS 业务或一个移动应用程序,赚一些钱。我们的结果会是这样的:
Our Result
它将对任何标签及其相关上下文进行实时分析,并在新推文出现时向您展示,同时附上一份情感。
够兴奋了吧?很好,我们开始吧。本文分为三个部分:
- 制作模型
- 制作 UI 界面(前端)
- 制作后端,获取实时数据,连接一切
1.模特的东西
虽然情感分析在自然语言处理中是一个非常常见的话题,但我现在只简要介绍一下模型架构,但稍后我会就此写一篇单独的帖子。
我使用了感知 140 数据集进行训练,它包含大约。160 万条推特。在通过规范化清理文本并删除以’ @ '开头的用户标签后,我使用了gensim
包的 Word2Vec 函数在整个语料库上训练它。由于语料库相当庞大,我有足够的数据来训练相当准确的嵌入,否则,我会使用预训练的矢量器。
#Test word embeddings
w2v_model.most_similar("hate")[('suck', 0.5254894495010376),
('stupid', 0.509635865688324),
('hat', 0.479534387588501),
('ugh', 0.4475134015083313),
('dislike', 0.44565698504447937),
('despise', 0.43604105710983276),
('fuck', 0.4104633331298828),
('annoy', 0.4004197418689728),
('ughh', 0.3961945176124573),
('fml', 0.39270931482315063)]
接下来,我使用了keras
标记器将输入数据转换成标记,并添加填充符以使所有输入长度相同。这是 NLP 中数据准备的标准过程。最后,我把准备好的数据传进了一个 LSTM 网络。
predict("@Nintendo I love your games!"){'score': 0.820274293422699}
最终的准确率竟然在 78.4% 左右,目前来看已经足够好了。整个实现是这里是
ACCURACY: 0.784396875
LOSS: 0.45383153524398806
最后,我保存了模型(作为一个. h5 文件)和经过训练的 Keras Tokenizer(作为一个. pkl 文件),以便以后在服务器脚本的推理过程中使用它们。您可以在此下载培训文件
**注意:**我实现了另一个模型,使用 1D 卷积代替 LSTM 网络进行比较,结果提供了几乎相似的结果。对于好奇的学习者,你可以在这里找到这个实现。
2.UI 前端的东西
我使用了 ReactJS 来构建接口。这是一个 Javascript 框架,通过创建组件并像乐高积木一样重用它们来促进模块化设计。每个组件都有其生命周期,因此如果某个组件的数据发生变化,只有该组件会刷新。这减轻了浏览器的负担,并减少了更新信息之间的延迟。
我不打算详述我是如何制作这个网站的,因为它只是基本的 CSS 和 Javascript,因此你可以直接研究库中的代码。然而,如果你有任何疑问,请在下面留下你的回复,我会很乐意为你解答。
你只需要知道
我们有一个名为**state**
的变量,它属于网站,这里的任何变化都会刷新组件。
this.state = {
hashtag: "",
options: {
colors: ['#F7464A', '#46BFBD', '#FDB45C'],
labels: ['Negative', 'Positive', 'Neutral'],
plotOptions: {
pie: {
donut: {
labels: {
show: true
}
}
}
}
},
series: [44, 55, 41],
tweets: [],
hashtag_desc: ""
}
**hashtag**
包含输入字段的值,**options**
值属于饼图的一些选项。我们只对一个功能感兴趣:
- 该函数在被调用时会将一个 GET 请求连同
**hashtag**
值一起发送到我们位于‘http://localhost:8000/analyzehashtag’的服务器。它需要以下形式的 JSON 响应:
{
...
data: {
positive: 43,
negative: 23,
neutral: 12
}
...
}
- 该函数还向公共维基百科 API 发送一个 GET 请求,以及
**hashtag**
值,以获取一些关于它的简短信息。 - 最后,该函数将另一个 GET 请求连同
**hashtag**
值一起发送到我们位于‘http://localhost:8000/gettweets’的服务器。它需要以下形式的 JSON 响应:
{
"results": [
{
"text": "Is it possible to wirelessly project my laptop to my #Xbox? #XboxOne [https://t.co/KMuSoD2C5j](https://t.co/KMuSoD2C5j)",
"username": "Xbox_One_Reddit",
"label": "Neutral",
"score": 0.5679275393486023
},
{
"text": "This year's #E3 had some big #XBOX news for the gaming industry. A glimpse at the future with Scarlet its Next Gen console, promising 4K & 8K gaming, and of course the franchise that started it all... #Halo Infinite announced!\n\nWhich was you favorite?? #E32019 #XboxE3 #Gaming [https://t.co/tykdIYezmr](https://t.co/tykdIYezmr)",
"username": "NrdRnx",
"label": "Positive",
"score": 0.9130105972290039
},
{
"text": "DOMED 💀 #apex #apexlegends #apexlegendsxbox #apexlegendsclips #apexlegendscommunity #apexlegendsplays #playapex #controllergang #xbox #mixer #twitch [https://t.co/itERG2vpaD](https://t.co/itERG2vpaD)",
"username": "gle_oh",
"label": "Negative",
"score": 0.26629960536956787
},
...
]
}
这些数据用于填充处理实时 tweets 的组件。
3.后端的东西
最后,我们进入这篇文章的核心。对于后端,我们将使用 Django 来创建它。
注意:如果你没有后端开发经验,我推荐用 Flask 代替 Django。Flask 对用户非常友好,你可以在几分钟内创建和我在这里做的一样的东西。我使用 Django 是因为我发现部署更容易一些,而且它很容易扩展到更复杂的应用程序。
你可以谷歌如何创建 Django 项目,或者跟随他们文档中给出的教程。完成后,它应该具有以下文件夹结构:
│ .gitattributes
│ db.sqlite3
│ manage.py
│
├───main_app
│ │ admin.py
│ │ apps.py
│ │ config.py
│ │ models.py
│ │ Sentiment_LSTM_model.h5
│ │ tests.py
│ │ tokenizer.pickle
│ │ twitter_query.py
│ │ views.py
│ │ __init__.py
│ │
│ ├───migrations
│ │
│ └───__pycache__
│ admin.cpython-36.pyc
│ config.cpython-36.pyc
│ models.cpython-36.pyc
│ views.cpython-36.pyc
│ __init__.cpython-36.pyc
│
└───twitter_django
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
└───__pycache__
settings.cpython-36.pyc
urls.cpython-36.pyc
wsgi.cpython-36.pyc
__init__.cpython-36.pyc
(而不是**main_app**
和**twitter_django**
,它们将是您选择的应用程序的名称)
Django 用“视图”的概念来封装负责处理用户请求和返回响应的逻辑。因此,我们收到的对服务器的任何请求都将在这里处理。我们使用**urls.py**
: 来连接视图
当我们在一个特定的 URL 端点接收到一个请求时,比如说“/gettweets”,它触发指定的函数——在本例中是“views.gettweets”。功能的逻辑写在**views.py**
中。
注意这几行:
global graph
graph = tf.get_default_graph()
model = load_model('main_app/Sentiment_LSTM_model.h5')
如果没有图表,您就无法运行您的模型来获得预测(因为 Tensorflow 的工作方式)。如果你试着跑,’ model.predict(…)'而没有指定图形,您将得到一个错误。因此,当你试图使用你的模型时,不要忘记加上:
with graph.as_default():
prediction = model.predict(...)
十二岁
如果你想从 Twitter 获取数据,Tweepy 就像是一个包。你可以使用pip.
来安装它,你所需要的只是一些独特的密钥。这些密钥可以通过在 Twitter 开发者网站注册一个应用程序来获得。
一旦完成,我们可以将tweepy
初始化为:
# Twitter
auth = tweepy.OAuthHandler(consumer_key,consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth,wait_on_rate_limit=True)
现在要获取 tweets,我们可以调用 Cursor 函数。我们使用了一个定制的搜索标签(从前端接收)并收集了相同的 tweets。
**注意:**我使用
*“ -filter:retweets”*
是为了只获得唯一的推文,否则,由于不同的转发频率,情感标签可能会有偏差。
tweepy.Cursor(api.search,q="#" + request.GET.get("text") + " -filter:retweets",rpp=5,lang="en", tweet_mode='extended').items(100)
制作一个 REST API
我们的服务器有两个重要的功能:
**analyzehashtag()**
—接受标签值,使用 tweepy 获得该标签的大量 tweets,并对每个 tweepy 执行情感分析。最后,通过简单地统计观察结果,计算积极、消极和中性推文在特定标签中的分布。
def analyzehashtag(request):
positive = 0
neutral = 0
negative = 0
for tweet in tweepy.Cursor(api.search,q="#" + request.GET.get("text") + " -filter:retweets",rpp=5,lang="en", tweet_mode='extended').items(100):
with graph.as_default():
prediction = predict(tweet.full_text)
if(prediction["label"] == "Positive"):
positive += 1
if(prediction["label"] == "Neutral"):
neutral += 1
if(prediction["label"] == "Negative"):
negative += 1
return JsonResponse({"positive": positive, "neutral": neutral, "negative": negative});
2.**gettweets()**
—这与第一个函数类似,但它不是计算分布,而是收集更少数量的推文并返回每个推文的结果。这样我们可以实时显示我们的模型的熟练程度,并检查它是否符合我们的常识。
def gettweets(request):
tweets = []
for tweet in tweepy.Cursor(api.search,q="#" + request.GET.get("text") + " -filter:retweets",rpp=5,lang="en", tweet_mode='extended').items(50):
temp = {}
temp["text"] = tweet.full_text
temp["username"] = tweet.user.screen_name
with graph.as_default():
prediction = predict(tweet.full_text)
temp["label"] = prediction["label"]
temp["score"] = prediction["score"]
tweets.append(temp)
return JsonResponse({"results": tweets});
现在,为了让我们的前端能够访问这些函数,我们将把这些函数作为 API。使用 Django REST 框架可以很容易地做到这一点。
只需使用pip install djangorestframework
安装,并在每个功能前添加[@api_view](http://twitter.com/api_view)([“GET”])
。(因为我们在这里只使用了 2 个 GET 请求,所以我在这里只使用 GET)。
不要忘记在settings.py
文件中添加以下内容。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'main_app'
]
结论
使用python manage.py runserver
运行服务器,享受洞察“人们对新选举的反应”,或者“人们喜欢赛博朋克 2077 预告片中的基努·里维斯客串吗”。发疯吧!
我的 GitHub 上有完整的代码和安装说明。如果你愿意,可以去看看。再见
拥有 3 年以上项目工作经验的初级数据科学家和软件开发人员。高度熟练的机器…
github.com](https://github.com/agrawal-rohit?tab=repositories)**
生活在机器人社会——一位世界领先的机器人专家的观点
与日本大阪大学智能机器人实验室主任石黑浩教授的 15 分钟。
我们谈到了他在制作机器人版本时学到的关键知识,与技术融合的意义,以及他对 HBO 著名剧集《西部世界》的看法。
你的愿景是什么?
我的动机是了解人类是什么。我对自己和人类社会非常感兴趣。我认为我们作为人类在这个世界上生存的目的就是思考人类。我们总是试图了解自己,这是在这个世界上生存的最重要的动力。
在我的案例中,通过使用计算机技术、机器人技术和机器人技术,我正在接近这些问题。
这是你建造人形或安卓机器人的原因,是为了在建造这些机器人的过程中了解我们自身的人性吗?
一旦我们创造了一个非常像人类的机器人,我们就可以把它附加到更重要的人性上,比如现在。我们在互相交谈,这是一次交谈,但交谈的真正意义是什么。
可能要进行一次谈话,我们需要思考一些事情,但是思考什么,对吗?有那么多模棱两可,不为人知的话。我想通过使用机器人来理解每一个对我们人类来说模糊但非常重要的概念。这就是我们实验室正在做的事情。
你说思维,就是说意识。你对意识有自己的定义吗?
意识是,思维,情感,欲望等等。这些概念是无法明确区分的。
我没有自己最初的定义,但是在研究中,我们基本上考虑三个层次,三层意识。首先是唤醒水平,然后是现象水平,最后是进入水平。
现象学的层次是感受性。例如,当我们看到日落时,我们被感动了。我们被日落的美景所感动。
在进入层面,我们可以考虑自己,我们可以感受自己。
这些层是紧密耦合的,我们不能分解它们。同时,这些意识水平与意图、欲望和情绪密切相关。
身体、大脑、思想之间有什么联系?
这对我们来说是非常重要的课题之一。本来我已经把 android 系统开发成了一个遥操作的 Android。这意味着如果我们通过互联网交谈,机器人可以像我一样行动。每个人都可以很容易地接受机器人身体作为他或她自己的身体。所以,我们实际上不需要身体和大脑之间的紧密联系。如果我们有一个机器人身体,我们的大脑可以接受机器人身体作为我们自己的身体,如果大脑可以控制身体的话。这是一个非常有趣的现象。
当我想到您构建的 Geminoid 时,您在这个过程中有哪些重要的见解?你学到了什么?
最重要的教训是,实际上,我不能接受我的第一个双子星座是我自己;但是其他人,学生和我的工作人员说这和我自己是一样的。
这意味着,我们不能对自己有客观的看法。我们不知道我们的脸、声音和行为。我发现人类基本上不可能有客观的观点。
这是我最感兴趣的发现。
因为我们有偏见,所以我们感知自己的世界和周围的人……
我们可以感知世界,但我们不能直接观察自己,我们需要使用镜子和录音机。一旦我们使用录音机,看着镜子,那就不同于我自己的辨认了。录制的声音听起来像另一个人。原则上,我们不能对自己有客观的看法和理解。
接下来,你可能想让 Geminoid 能够与人交流和互动,在这一点上,它变得更加复杂。
我们还没有研究人格,但是在研究人格之前,我们需要发展与他人对话的能力和功能。对话式自主机器人非常困难。现在,我们将重点放在对话功能上,我们采取了许多方法,如聊天机器人方法或更高级的非正式讨论。我们关注的是基于意图和欲望的更深层次的对话。
什么时候我会和 Geminoid 说话,而我不能确定我是在和你说话还是和你的机器人版本说话?
也许以后吧!我们正在越来越多地改进机器人。还没有!
你怎么看待从技术上拔掉插头?你认为这重要吗?你认为这在未来会变得不那么重要吗?
我认为思考人性是非常重要的,但从根本上说,我们人类有两种进化方式。
一个是基因;另一个是技术。因为科技,我们可以成为人类。如果我们不想使用技术,我们想成为猴子。我们不能把技术和我们人类分开。人类是使用技术和工具的动物。我们需要研究两个方面,人类的动物方面和人类的技术方面。但更重要的是,我们可以利用新技术开发更好的能力,这就是人类进化的方式。
需要克服的最大的坎是什么?
如果我们看看智能手机,一个国家无法用智能手机技术开发实用系统,iPhones 和其他智能手机是世界商业产品。他们需要有一个大市场来拥有一个更强大和更便宜的设备。对于机器人,我们需要有更多的技术。机器人需要在智能手机上添加执行器和传感器,然后我们才能拥有一个机器人。
在这种情况下,我们需要一个对社会产生更大影响的更大的市场。否则,我们就无法进行大规模生产,机器人也会非常昂贵。这是机器人社会最困难的一面。
让我们接触一下 乌托邦和反乌托邦的场景。你更积极还是更消极?
我不能说这是好是坏,因为未来就是未来。
我们总能在那里找到快乐。开心和不开心是相对的。
更重要的是扩展我们的能力和生存。如果我们开发更多的技术,我们将有更多的机会在这个世界和这个宇宙中生存。这对我们人类来说是最重要的事情。乌托邦和反乌托邦只是相对的尺度。如果你能享受与否,那取决于你。
一种观点是在我们的大脑中实现微芯片,以跟上机器人……
这是自然的。技术是进化中非常重要的一部分。看看我们现在在做什么,每个人都继续使用他们的智能手机,这几乎就像一个植入物。
你有什么个人的技巧或者日常做的套路来想出新点子吗?
我总是试着去想很多事情。这是一种内在的声音。所以总有人在我脑中说话。当我在浴室洗澡的时候,当我用吹风机的时候,总会有一些想法冒出来。如果我摸我的头,我可以进入我大脑中的中性状态,然后会有一些想法出现。
保持大脑的中立状态是非常重要的。也许这类似于心理化。对我来说很简单,我只需要用一个吹风机,然后我就可以精神化了。
想象一下,以你现在所拥有的知识和经验,2018 年你将年满 18 岁。你会怎么做?你会获得什么技能?
尽管如此,我还是会做同样的事情。我目前的动机是了解我自己、人类和社会。这是一个很根本的基本的东西。我也会这么做,但我可能会接受更多的挑战。我可能会开另一家公司。如果我年轻,我可以接受更多的挑战。
我认为我年轻的时候是一名优秀的程序员。如果我现在 18 岁,我会连接到软件世界,自己开发人工智能。
所以,技术技能与哲学、艺术、心理学等诸多领域的结合……
但是最有效的方法是建设性的方法。创造一个有意识的代理人。在那之后,我们可以谈论心理学、哲学等等,但是没有代理人,我们无法进行讨论。我称这种方法为“建设性方法”。
你知道西部世界系列吗?
是的。我不喜欢这个想法,它是完全错误的。如果我们杀死一个类似人类的机器人,那就是谋杀。我们没有那么疯狂,所以方法完全错误。
有什么可以推荐的电影?
两百周年纪念人,由罗宾·威廉姆斯扮演。我喜欢那部电影。活了两个世纪的人。
喜欢这篇文章吗?这是另一个
领先的人工智能专家乔安娜·布赖森就过度拟人化人工智能、偏见和安全人工智能的系统设计进行了采访。
medium.com](https://medium.com/omdena/do-we-need-to-dehumanize-artificial-intelligence-78d2f28c24fb)
关于作者:
迈克尔·布哈特是一位 AI 旅行企业家(40 多个国家)。他帮助 AI 爱好者通过真实世界的项目 获得动手技能。除了艾禾关于思维转变、冥想技巧和旅行者身体锻炼的博客。
在LinkedIn, 中 和insta gram。
加载&%$*#检查点!
成功的 Udacity 项目检查点加载的提示和技巧
Photo by Vidar Nordli-Mathisen on Unsplash
加载那个检查点有问题吗?
我也是。
成为 Udacity 脸书 AI PyTorch 挑战赛的一员真是太棒了,一路上我学到了很多。完成最后的挑战后,我想分享一些技巧和诀窍,这些技巧和诀窍使我能够成功地将深度学习图像分类器项目的检查点提交到 Udacity 工作区。
最重要的是,遵循实验室的指示!我希望你知道这一点,但我把它放在这里以防万一…
保存您的检查点
如果您正在尝试在实验室中加载检查点,我假设您已经保存了检查点,但您确实需要保存检查点,因为这是您将在实验室中加载的内容!Torch.save 在这里非常方便。例如,这里有一个可能的检查点:
checkpoint = {'input_size': 2208,
'output_size': 102,
'epochs': epochs,
'batch_size': 64,
'model': models.densenet161(pretrained=True),
'classifier': classifier,
'scheduler': sched,
'optimizer': optimizer.state_dict(),
'state_dict': model.state_dict(),
'class_to_idx': model.class_to_idx
}
torch.save(checkpoint, 'checkpoint.pth')
只装你需要的东西。你可能不需要我在这个检查点加载的所有信息,但是我把上面的一个作为例子,这样你可以看到一些选项。在实验室中加载检查点之前,应该完成检查点保存。
确保您知道您的检查点保存在哪里,以便您可以将该文件上传到 Udacity 工作区!
在 Colab 工作?
如果您正在使用 Google Colab,并且将您的 Google Drive 安装到您的笔记本上(这里有一篇很棒的文章,如果您刚刚开始使用 Colab 并且不知道我在说什么,您可以通过运行以下命令将您的检查点直接移动到您的 Google Drive 中:
model_save_name = 'classifier.pth'
path = F"/content/gdrive/My Drive/{model_save_name}"
torch.save(model.state_dict(), path)
请确保文件路径正确无误!
*如果您决定将您的检查点保存到您的 Google Drive,您实际上可以将它从那里移动到 Udacity 的工作区,方法是转到您的 Google Drive,获取可共享的链接文件,并找到您的文件 ID。您只需要文件 ID:它将是“id=”后面的一长串字母和数字。然后转到 Udacity 实验室笔记本运行
!pip install gdown==3.6.0
安装 gdown。那就跑
ckpt_file_id = "YOUR-FILE-ID-GOES-HERE"
(确保用您的实际文件 ID 替换“YOUR-FILE-ID-GOES-HERE ”!)
最后
!gdown [https://drive.google.com/uc?id={](https://drive.google.com/uc?id={1kX4IwGUB88x9CTHAfhsAz7PljqnKZdEp)ckpt_file_id}
这将下载您的文件并将其放在 Udacity 实验室中。
在 Udacity 工作区中
如果您没有使用 Google Drive,那么当您到达实验室时,您需要点击“上传”按钮,上传您的检查点文件。确保它一直到 100%,否则你可能会得到一个损坏的文件。
以下是您提交项目所需的重要信息:
你需要加载你的参数。我从
ckpt = torch.load('checkpoint.pth')
ckpt.keys()
这样我就可以快速看一下我的钥匙。然后我运行我的进口。
from collections import OrderedDictimport torch
import torch.nn as nn
from torchvision import models
import os
from torchvision import datasets, transforms
这里有一个非常重要的细节:如果您还没有定义图像大小,请运行以下命令:
image_size = 224
# Values you used for normalizing the images. Default here are for
# pretrained models from torchvision.
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
之后,您可以创建一个检查点加载函数,并使用它来加载模型。
def load_checkpoint(filepath):
checkpoint = torch.load(filepath, map_location='cpu')
model = checkpoint['model']
model.classifier = checkpoint['classifier']
model.load_state_dict(checkpoint['state_dict'], strict=False)
model.class_to_idx = checkpoint['class_to_idx']
optimizer = checkpoint['optimizer']
epochs = checkpoint['epochs']
for param in model.parameters():
param.requires_grad = False
return model, checkpoint['class_to_idx']model, class_to_idx = load_checkpoint('/home/workspace/checkpoint.pth')
你看到上面写着
map_location='cpu'
和
strict=False
?
你想包括那些!
不是开玩笑。Udacity 的工作空间没有连接 GPU,因此您需要确保指定了 CPU。您还希望使用“strict=False ”,以避免遇到任何不必要的版本兼容性问题。
仔细检查分类器的文件路径。正如你在上面看到的,我的是
model, class_to_idx = load_checkpoint('/home/workspace/checkpoint.pth')
你真的需要有正确的道路!如果你看一下测试单元上面的说明,几乎可以肯定你的文件路径应该是什么样子。Udacity 真的希望你成功,所以一定要认真看说明书!
最后
将所有内容放入一个单元格中!将每一行代码放入提供的测试单元中。许多学生在做了一个简单的改变后,最终成功提交了项目。去做吧!
现在测试代码并开始您的项目!这个项目是严格的通过/失败,所以如果你通过了,你就完成了!恭喜你!!!
现在去庆祝吧!
Photo by Jayson Hinrichsen on Unsplash
使用 AWS Lambda 和 CloudWatch 事件加载 Reddit 帖子
上个月,我完成了 Reddit 分类项目,通过抓取 Reddit 网站收集数据,然后建立了一个二元分类器来识别给定帖子的来源。现在,当我有一些空闲时间时,我决定检查一下那个项目,并做一些改进。之前,我使用 cron 在 AWS EC2 实例上运行 python 脚本。但这次我决定探索更多的 AWS 服务,并将该脚本转换为由 CloudWatch 触发的 Lambda。
那么,我们开始吧。我们将创建一个 AWS lambda,它将使用 Reddit api 加载 Reddit 帖子,并将它们存储到 S3 桶中。
首先,我们定义一个 bucket 名称和一个要加载的主题列表。然后我们定义一个 lambda 的主方法——“lambda _ handler”,它接受两个参数——事件和上下文。这个方法是我们 lambda 的一个入口点,CloudWatch 会每隔 15 分钟触发一次。该方法将遍历主题列表,并为每个主题加载 reddit 帖子。如果出现异常,我们会捕获它,记录下来,然后继续下一个主题。
接下来,让我们看看加载 reddit 帖子的 main 方法。首先,我们检查一个 csv 文件与以前加载的职位已经存在于 S3。如果是这样,我们将这些帖子加载到一个列表中。我们从列表中提取最后保存的帖子的 id,使用“before”参数继续加载该 id 之后的所有新帖子。如果在 S3 没有现存的文件,这意味着我们还没有加载任何帖子。在这种情况下,我们将使用“after”参数通过 Reddit api 加载所有可用的帖子。你可以点击这个链接阅读更多关于如何使用 Reddit api 的内容:https://www.reddit.com/dev/api/它解释了如何使用“之前”/“之后”参数进行分页。最后,我们将所有帖子转换为 csv 字符串,并保存到 S3。
以下是我们如何从 S3 加载保存的帖子。我们使用 AWS boto3 客户端访问 S3。我们从 S3 读取一个文件,将它分割成单独的行,并将它们传递给 python csv 阅读器库。然后我们使用列表理解将每个解析的行转换成一个元组。
下面是使用 python 请求库加载帖子并从 json 响应中提取必填字段的方法。load_posts 方法使用 while 循环来持续加载 reddit 帖子。首先,它将 pagingId 作为 None 传递,以加载第一页。之后,它从加载的页面中提取下一个 pagingId,并将其与下一个请求一起传递。它一直这样做,直到 pagingId 变成 None。意味着 reddit api 没有剩下任何页面。
你可以在我的 Github 上找到完整的代码
现在,当我们准备好 lambda 代码后,我们需要创建一个 zip 文件来部署到 AWS。当我第一次尝试在 AWS 上运行这段代码时,它不起作用,因为 lambda 没有 requests python 库。结果是,在将 lambda 脚本部署到 AWS 之前,我们必须将所有必需的依赖项和 lambda 脚本一起添加到档案中。让我们在 cmd 中运行以下命令:
$ **mkdir lambda**
$ **cd lambda**
$ **pip install --target ./ requests .** //install requests library
$ **zip -r ../reddit_lambda.zip .** //archive the folder
$ **cd ..**
$ **zip -g reddit_lambda.zip reddit_lambda.py** //add lambda script
这将创建一个名为 reddit_lambda.zip 的归档文件,其中包含请求依赖项和我们的 lambda 脚本。
现在我们将练习使用 AWS Cli 部署我们的 lambda 函数,并每隔 15 分钟触发一次。准备好在命令行中运行许多命令。
首先,我们必须为我们的 lambda 创建一个名为 reddit_lambda_role 的执行角色:
$ **cat lambda_assume_role_policy.json**
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}
$ **aws iam create-role --role-name reddit_lambda_role --assume-role-policy-document file://lambda_assume_role_policy.json**
这个假设策略文档允许角色由我们帐户中的任何 lambda 假设。目前这个角色没有权限——这意味着即使 lambda 假设它没有权限做任何有用的事情,比如访问 S3 来保存对象。要解决这个问题,让我们创建一个策略:
**$ cat lambda_role_policy.json** {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObjectAcl"
],
"Resource": "arn:aws:s3:::**your-bucket-name**/*"
},{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:eu-west-1:**your-account-id**:*"
},{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "*"
}]
}$ **aws iam create-policy --policy-name reddit_lambda_policy --policy-document file://lambda_role_policy.json**
这个名为 reddit_lambda_policy 的策略授予访问 S3 的权限,以加载和保存文件,并将日志保存到 CloudWatch。现在,让我们将此策略附加到一个角色:
$ **aws iam attach-role-policy --role-name reddit_lambda_role --policy-arn arn:aws:iam::*your-account-id*:policy/reddit_lambda_policy**
现在让我们在 AWS 中创建一个名为 RedditLoader 的 lambda:
$ **aws lambda create-function --region us-east-1 --function-name RedditLoader --zip-file fileb://reddit_lambda.zip --role arn:aws:iam::*your-account-id*:role/reddit_lambda_role --handler reddit.lambda_handler --timeout 900 --runtime python3.7**
此时,您可以转到 AWS 控制台,打开 lambda 部分并手动运行这个 Lambda。它应该能够加载 reddit 帖子,并将其存储在 S3。但是我们的目标是使用 CloudWatch 事件每 15 分钟触发一次这个 lambda。
让我们创建一个名为 RedditLoadingRule 的预定事件规则:
$ **aws events put-rule --name RedditLoadingRule --schedule-expression 'rate(15 minutes)'**
现在,我们向 lambda 添加由规则中的事件触发的权限:
$ **aws lambda add-permission --function-name RedditLoader --statement-id RedditScheduleEvent --action 'lambda:InvokeFunction' --principal events.amazonaws.com --source-arn arn:aws:events:us-east-1:*your-account-id*:rule/RedditLoadingRule**
向我们的规则添加一个目标,以便它知道每 15 分钟触发什么:
$ **aws events put-targets --rule RedditLoadingRule --targets "Id": "1","Arn":"arn:aws:lambda:us-east-1:*your-account-id*:function:RedditLoader"**
仅此而已。现在,CloudWatch 将每隔 15 分钟发送一个事件,触发我们的 lambda。
所有的 AWS 命令可能看起来令人生畏,但是实际上一旦你学会了如何通过 AWS cli 使用基本命令,你将很快适应只使用 cli 与 AWS 交互。这是一项非常好的技能,可以打开强大自动化的大门。
在下一篇文章中,我们将看看如何使用 SageMaker 来训练和部署使用我们来自 S3 的 reddit 帖子的模型。
小企业银行贷款违约预测
Photo: https://thelendersnetwork.com/minimum-credit-score-needed-personal-loan/
银行贷款给公司,以换取还款承诺。有些人会拖欠贷款,因为某些原因无法偿还。银行维持保险以减少他们在违约时的损失风险。保险金额可以覆盖全部或部分贷款金额。
对于这项任务,银行希望根据公司的财务信息预测哪些公司会拖欠贷款。提供的数据集由贷款相关信息组成,如贷款金额、期限和州。此外,还有公司信息,如员工数量、运营部门等。
目标
为了预测一家公司是否会拖欠贷款,我尝试了两种不同的机器学习算法:逻辑回归和随机森林。这项作业的指示是使用准确性作为评估标准。然而,在这种情况下,精确性是至关重要的,因为我们希望将贷款违约的可能性降至最低。
见解/探索性数据分析
快速浏览一下数据就能发现一些真知灼见:
1 .贸易公司是最大的客户群
2。较小的公司更容易违约。贷款期限不影响违约的可能性。拖欠贷款的客户只有一半多
有了这些信息,我开始清理数据,并使用洞察力生成新的特征。目标是预测违约状态,0 表示没有违约,1 表示违约。
型号
为了确定哪些信息对于模型的良好运行是必不可少的,我查看了特征的重要性,并重复了特征工程的步骤。
逻辑回归是我的首选模型,因为它的时间复杂度低。该模型的准确率约为 70% 。这也意味着 13% (109/821)的客户没有进行任何还款,但预计不会违约,也就是假阳性。
随机森林是我的下一个模型。它的初始准确度分数是 99%,但是当引入看不见的数据时(验证是 90%),过度拟合将导致较差的预测。调整模型后,最终模型的精度为 94% ,不再过度拟合。维奥拉。我们设法将假阳性的数量从 13%降低到 3%。
结论
没有一种方法可以确定客户是否会停止还款。但是,贷款期限、行业、公司规模等因素都会影响他们的还款能力
这个项目的代码可以在我的 Github 上找到。如果你愿意联系,可以通过 LinkedIn 联系我。
本地数据应该由本地所有
数据所有权的新模式意味着我们应该支持本地数据的本地所有权
Photo by Pawel Nolbert on Unsplash
有两个经济对话势头越来越大,看起来即将发生冲突。首先是本地财富创造:我们如何确保本地投资创造的财富留在当地社区并由当地社区控制?第二个是数据所有权:我们当前的自由放任数据所有权模式(将控制权和监督权交还给利益相关方)有哪些替代方案?
什么是本地数据?
本地数据是指主要在应用于产生价值的社区时产生价值的数据。在某种程度上,所有数据都是本地的。例如,当你的喜好和兴趣被用来更准确地为你投放广告时,它们是最有价值的。如果你的数据被用来针对其他人,这些数据可能是有用的,但它可能会变得不那么有价值。因此,本地数据的值不仅仅是与本地相关的*,而是主要与本地相关的*。**
建立联系
随着数据成为地球上最有价值的资源,这一价值的大部分将来自其在地方一级的应用,无论是在个人、城市还是国家一级。
地方财富的创造与数据所有权密切相关。例如,考虑运输数据。优步和谷歌地图等服务收集了城市内出行者的大量数据。例如,谷歌会让用户选择次优路线来标出他们不太了解的城市部分。这些数据随后被用于改善其服务。谷歌可以告诉你交通延误、公共交通路线、指路、估计行程时间。优步使用类似的数据来预测汽车需求,将客户的旅程与理想的司机联系起来,从而确保司机最终到达满足预测需求的最佳地点。
这里发生了很多事。首先,所有这些数据都是本地的。虽然了解伦敦的交通模式可能有助于测试以巴黎为模型的机器学习算法,但没有用户能够用伦敦地图在巴黎导航。
其次,所有这些数据都是在本地生成的。这在谷歌和次优方向的案例中很明显,但也有不太明显的来源。例如,如果每天有 1000 人在同一条街上开车,谷歌将能够构建一个旅行时间的正态分布,并预测这条街何时最繁忙。然后,通勤者正在生成关于他们的本地环境的数据,这些数据被非本地实体捕获。
第三,考虑数据访问。用户不直接访问这些数据,而是通过应用程序体验这些数据。这对普通人来说是好事,但人们应该想知道,为什么像优步这样遥远的公司比城市办公室的民选官员更了解和控制一个城市的交通网络。
提取,血统
这些数据的价值在本地产生,但流向拥有和控制这些数据的人。这些数据所有者(如谷歌和优步)可能不是本地人,也不太可能在城市的交通网络中有任何既得利益。例如,即使网络非常差,这些服务仍然可以具有竞争力,因为它们更了解差的网络。他们的优势不是来自投资,而是来自数据的提取和数据创造的价值机会。
地方政府没有理由不收集和分析这些数据,也没有任何东西阻止他们创建自己的应用程序来传递这些服务。通过让本地机构管理本地数据,这些数据被那些对其有既得利益的人使用,同时这些数据的价值仍然由本地持有。
然而,有人可能会争辩说,价值总是本地拥有的——它是用户从服务中体验到的便利。但是,是吗?例如,虽然谷歌可能不会对他们的服务收费,但他们确实会用他们的服务做广告。例如,在谷歌地图上,一家企业可以付费出现在地图上。如果本地数据归本地所有,广告可能会被取消,或者——更有可能的情况是——向谷歌付费让其出现在地图上的本地企业可能会向当地政府付费(可能会降低费率),从而将财富留在当地经济中。
我们还应该记住,地方政府不是一个盈利企业。随着利润从桌面上消失,本地数据可以得到不同的分析,释放出新的价值来源。例如,谷歌没有从利用当地数据规划更好的公交路线中获利,但当地政府肯定会从这些见解中发现价值。
数据所有权的新模式
目前,对数据所有权新模式的研究确实令人兴奋。不管是好是坏,我们当前的数据所有权自由放任模式正在受到挑战,像 T2 开放数据倡议这样的组织已经开始在替代方法方面做了出色的工作,比如数据信托。
数据信托只是一种模式,其中包括访问和应用在内的数据管理任务交给了一组专家受托人。我们可能会看到数据合作社的建立(个人汇集他们的数据,所有人分享它创造的价值)或数据共享(个人汇集他们的数据,任何人都可以访问)。我们甚至可能会看到更大的项目的建立,如集体透明(本质上,一个大规模的数据共享),可能通过区块链技术来促进。
这些模型表明,有可能以不同的方式想象数据所有权,而本地财富创造展示了本地数据的本地所有权可以带来的优势。尽管硅谷服务如此便利,但没有理由相信我们不能在地方层面实现同样的(如果不是更好的话)目标,创造财富和新的见解,造福当地经济和人民。
局部模型解释:介绍
本文是我关于模型可解释性和可解释人工智能系列文章的延续。如果你还没有读过前两篇文章,我强烈推荐你去读一读。
本系列的第一篇文章,《机器学习模型解释入门》,涵盖了模型解释的基础知识。第二篇文章,‘动手全球模型解释’,详细介绍了全球模型解释,以及如何使用 Python 将其应用于现实世界的问题。
在本文中,我们将通过深入到本地模型解释来继续我们离开的地方。首先,我们将看看什么是局部模型解释,以及它可以用于什么。然后,我们将深入研究两种最流行的方法的理论,Lime(局部可解释模型不可知解释)和 Shapley 值,并应用它们来获得关于心脏病数据集上的个体预测的信息。
什么是局部模型解释?
局部模型解释是一套旨在回答以下问题的技术:
- 模型为什么做出这个具体的预测?
- 这个特定的特征值对预测有什么影响?
使用我们的领域专业知识,结合使用本地模型解释获得的知识,我们可以对我们的模型做出决定,比如它是否适合我们的问题,或者它是否没有按照预期发挥作用。
目前,使用本地模型解释变得越来越重要,因为公司(或个人)必须能够解释他们模型的决策。尤其是当它被用于需要大量信任的领域时,如医药或金融。
LIME(局部可解释的模型不可知解释)
尽管被广泛采用,机器学习模型仍然主要是黑箱。然而,理解预测背后的原因在评估信任时非常重要,如果一个人计划根据预测采取行动,或者在选择是否部署新模型时,信任是最基本的。— 里贝罗、马尔科·图利奥、萨梅尔·辛格和卡洛斯·盖斯特林。“我为什么要相信你?”。
Figure 2: Lime Example
概念和理论
Lime,局部可解释模型不可知,是一种局部模型解释技术,使用局部代理模型来近似底层黑盒模型的预测。
局部代理模型是可解释的模型,如线性回归或决策树,用于解释黑盒模型的单个预测。
Lime 通过从感兴趣的数据点生成新的数据集来训练代理模型。它生成数据集的方式因数据类型而异。目前 Lime 支持文本、图像和表格数据。
对于文本和图像数据,LIME 通过随机打开或关闭单个单词或像素来生成数据集。对于表格数据,LIME 通过单独置换每个要素来创建新样本。
LIME 学习的模型应该是黑盒模型的良好局部近似,但这并不意味着它是良好的全局近似。
优势
LIME 是分析预测的好选择,因为它可以用于任何黑盒模型,无论是深度神经网络还是 SVM。此外,LIME 是唯一适用于表格、文本和图像数据的解释技术之一。
不足之处
感兴趣的数据点的邻域的定义非常模糊,这是一个很大的问题,因为这意味着您必须为大多数应用程序尝试不同的内核设置。
此外,解释可能不稳定,这意味着对非常接近的数据点的解释可能变化很大。
关于 LIME 和可解释人工智能的更多信息,一般来说,请查看 Christoph Molnar 的优秀书籍 T2 的可解释机器学习。
示例和解释
Lime 可以在 Python 中与 Lime 和 Skater 包一起使用,这使得 LIME 与流行的机器学习库(如 Scikit Learn 或 XGBoost )的模型一起使用变得非常容易。在这篇文章中,我将研究心脏病数据集,一个简单的分类数据集。
为了分析像心脏病数据集这样的表格数据集的预测,我们需要创建一个 LimeTabularExplainer。
现在,我们可以使用 explain _ instace 方法来解释单个预测。
Figure 3: Lime Output
这里我们可以清楚地看到哪些特征值影响了预测。对于该数据点,cp 和 oldpeak 值对预测的负面影响最大。
有关如何使用 Lime 的更多示例,请查看自述文件中的“教程和 API”部分。
沙普利值
沙普利值是一种根据玩家对总支出的贡献将支出分配给玩家的方法。但是这和机器学习有什么关系呢?
概观
在机器学习的情况下,“游戏”是对数据点的预测任务。“增益”是预测减去所有实例的平均预测,而“玩家”是数据点的特征值。
Shapley 值是一个特性值在所有可能的联合中的平均边际贡献。这是一种解释单个预测的极好方法,因为它不仅给出了每个特征值的贡献,而且这些贡献的大小也是正确的,这与 LIME 等其他技术不同。
要获得更多关于 Shapley 值及其计算方法的信息,请务必查看 Christoph Molnar 的书“可解释的机器学习”的 Shapley 值部分。
优势
当使用 Shapley 值时,预测和平均预测在实例的特征值之间公平分布。这使得 Shapley 值非常适合需要精确解释的情况。此外,Shapley 值是唯一有坚实理论支持的局部解释方法。
不足之处
Shapley 值需要大量的计算时间。大多数情况下,只有近似解是可行的。另一个缺点是,您需要访问数据来计算 Shapley 值。这是因为您需要数据来替换感兴趣的部分实例。只有创建看起来像真实数据实例的数据实例,才能避免这个问题。
示例和解释
我们可以使用由 Scott Lundberg 创建的优秀的 shap (SHapley 附加解释)包在 Python 中使用 Shapley 值。
shap 包可用于多种模型,如树或深度神经网络,以及不同类型的数据,包括表格数据和图像数据。
要对心脏病数据集使用 shap,我们可以创建一个 TreeExplainer,它可以解释像随机森林一样的树模型的预测,然后使用 shap_values 和 force_plot 方法来分析预测。
在图像中,我们可以看到每个特征值如何影响预测。在这种情况下,预测值为 0,这意味着患者没有心脏病。最重要的特征值是 cp=0 和 ca=3。这由箭头的长度表示。
结论
局部模型解释是一套试图帮助我们理解机器学习模型的个体预测的技术。LIME 和 Shapley 值是理解个体预测的两种技术。
Lime 非常适合快速查看模型正在做什么,但是在一致性方面有问题。Shapley value 提供了一个完整的解释,这使得它比 Lime 更精确。
这就是这篇文章的全部内容。如果你有任何问题或者只是想和我聊天,请在下面留下评论或者在社交媒体上联系我。如果你想获得我博客的持续更新,请确保在 Medium 上关注我,并加入我的时事通讯。
具有深度学习的定位和物体检测
定位和目标检测是计算机视觉中的两个核心任务,因为它们被应用于许多现实世界的应用中,例如自主车辆和机器人。所以,如果你想在这些行业工作,成为一名计算机视觉专家,或者你想开发一个相关的产品,你最好能很好地掌握它们。但是它们是什么呢?物体检测和定位意味着什么?为什么我们把它们归为一类?
重要的事情先来。让我们快速回顾一下最常用的术语及其含义,以避免误解:
- 分类/识别:给定一幅带有物体的图像,找出那个物体是什么。换句话说,从一组预定义的类别中将其分类。
- **定位:**找到物体所在的位置,并在它周围画一个边界框
- 物体检测:对图像中的所有物体进行分类检测。给每个对象分配一个类,并围绕它画一个边界框。
- 语义分割:将图像中的每一个像素按照其上下文分类,这样每一个像素都被分配到一个对象
- 实例分割:将图像中的每一个像素归为一类,这样每一个像素都被分配给一个对象的不同实例
但是,请记住,这些术语在科学界没有明确的定义,因此您可能会遇到其中一个具有不同含义的术语。在我的理解中,这些是正确的解释。
当我们了解基本术语后,是时候做一些定位和物体检测了。我们怎么做呢?这些年来有许多方法,但自从深度学习到来后,卷积神经网络成为了行业标准。记住我们的目标是对物体进行分类和定位。但是我们确定只有一个物体吗?有没有可能有两个或者三个或者十五个物体?事实上,大多数时候是这样的。
这就是为什么我们可以把我们的问题分成两个不同的问题。在第一种情况下,我们知道对象的数量(我们将该问题称为分类+定位),而在第二种情况下,我们不知道(对象检测)。我将从第一个开始,因为它是最简单的。
分类+本地化
如果我们只有一个对象或者我们知道对象的数量,这实际上是微不足道的。我们可以使用一个卷积神经网络,并训练它不仅对图像进行分类,而且为边界框输出 4 个坐标。这样,我们将本地化视为简单的回归问题。
例如,我们可以借用一个经过充分研究的模型,如 ResNet 或 Alexnet,它由一堆卷积、池化和其他层组成,并重新调整全连接层的用途,以产生与类别无关的边界框。它如此简单,以至于我们怀疑它是否会有结果。实际上它在实践中运行得很好。当然,您可以使用它并修改架构以服务于特定的问题或增强其准确性,但主要思想仍然存在。
请务必注意,为了使用这个模型,我们应该有一个训练集,其中包含为类和边界框注释的图像。而且做这样的标注也不是最好玩的。
但是如果我们事先不知道物体的数量呢?然后我们需要进入兔子洞,谈论一些核心的东西。你准备好了吗?之前要不要休息一下?当然,我明白,但是我警告你不要离开。这就是乐趣的开始。
目标检测
我开玩笑的。关于将要讨论的架构没有什么核心的东西。所有的是一些聪明的想法,使系统不能容忍输出的数量,并减少其计算成本。因此,我们不知道图像中对象的确切数量,我们希望对所有对象进行分类,并在它们周围绘制一个边界框。这意味着模型应该输出的坐标数量不是常数。如果图像有两个物体,我们需要 8 个坐标。如果它有 4 个对象,我们需要 16 个。那么我们如何建立这样一个模型呢?
传统计算机视觉的一个关键思想是区域提议。我们使用经典 CV 算法(如边缘和形状检测)生成一组可能包含对象的窗口,并且我们仅将这些窗口(或感兴趣的区域)应用于 CNN。要了解更多关于如何提议区域的信息,请务必查看此处的。
这是一篇基础的论文的基础,该论文介绍了一种叫做 RCNN 的新架构。
R-CNN
给定具有多个对象的图像,我们使用提议方法(在 RCNN 的情况下,这种方法称为选择性搜索)生成一些感兴趣的区域,并将这些区域弯曲成固定的大小。我们将每个区域转发到卷积神经网络(如 AlexNet),它将使用 SVM 为每个区域做出分类决定,并预测每个边界框的回归。该预测是对所提议的区域的校正,该区域可能处于正确的位置,但不是精确的大小和方向。
虽然这个模型产生了很好的结果,但是它有一个主要的问题。这是非常缓慢和计算昂贵的。想象一下,在一般情况下,我们产生 2000 个区域,我们需要将它们存储在磁盘中,我们将它们中的每一个都转发到 CNN 中多次传递,直到它被训练好。为了解决其中的一些问题,该模型的一个改进被称为“快速 RCNN”
快速 RCNN
这个想法很简单。我们将整个图像传递一次,并产生一个特征图,而不是将所有区域逐一传递到卷积层。然后,我们像以前一样(使用一些外部方法)将区域建议投影到特征图上。现在,我们在特征图中有了区域,而不是原始图像,我们可以在一些完全连接的层中转发它们,以输出分类决策和边界框校正。
请注意,区域投影建议是使用特殊层(ROI 层)实现的,它本质上是一种最大池,池大小取决于输入,因此输出总是具有相同的大小。有关 ROI 层的更多详细信息,请查看这篇伟大的文章。
更快的 RCNN
我们可以更进一步。使用从卷积层产生的特征图,我们使用区域提议网络而不是依赖于外部系统来推断区域提议。一旦我们有了这些建议,剩下的过程与 Fast-RCNN 相同(前进到 ROI 层,使用 SVM 分类并预测边界框)。棘手的部分是如何训练整个模型,因为我们有多个任务需要处理:
- 区域提议网络应该为每个区域决定它是否包含对象
- 并且它需要产生边界框坐标
- 整个模型应该对对象进行分类
- 并且再次预测边界框偏移
如果你想了解更多关于训练部分的内容,你应该检查一下最初的论文,但是为了给你一个概述,我们需要利用一个多任务损失来包括所有 4 个任务,并将这个损失反向传播到网络。
顾名思义,FasterRCNN 比以前的模型快得多,是大多数现实应用程序的首选。
定位和目标检测是一个非常活跃和有趣的研究领域,因为现实世界中的应用需要在计算机视觉任务(自动驾驶汽车、机器人)中具有出色的性能。公司和大学定期就如何提高准确性提出新的想法。
存在另一类用于定位和对象检测的模型,称为单次检测器,其在过去几年中变得非常流行,因为它们甚至更快并且通常需要更少的计算成本。当然,它们不太精确,但它们非常适合嵌入式系统和类似的耗电应用。
但是要了解更多,你必须等待第 2 部分…
如果您有任何想法、评论、问题或者您只想了解我的最新内容,请随时在Linkedin,Twitter,insta gram,Github或在我的
原载于 2019 年 3 月 25 日sergioskar . github . io。
使用自定义视觉人工智能中的对象检测来定位和识别蜂窝单元
Photo by Scott Hogan on Unsplash
从 7 月 31 日到 8 月 1 日,波多黎各有一场黑客马拉松,由跨学科定量生物学项目在波多黎各大学里约皮得拉斯校区 (UPRRP)主办。黑客马拉松在波多黎各 Bayamon 的一个合作空间 Engine 4 举行。
Hackathon Flyer
黑客马拉松的重点是寻找跨学科和定量(IQ)生物学问题的创造性解决方案。
我和Alejandro Armas(Alejandro Armas)Carlos Navea和 Giovanni Cortes 一起参加了这次黑客马拉松,他们都是 IQ 生物学暑期实习的聪明参与者。我们的团队名称是“蜂巢”。我们得到了 UPRRP 大学计算机科学系副教授雷米·梅里特教授的指导。
我们解决了两个问题:
- 通过 webapp 简化研究人员的数据输入。
- 使用对象检测自动蜂窝单元识别和计数。
本文主要讨论后者。
我们为黑客马拉松实现的代码可以在 https://github.com/alejandroarmas/PRhacks 的 GitHub 上找到。用于准备物体检测数据的代码在mlTool
文件夹中。
问题陈述
波多黎各大学蜜蜂研究人员的一些职责是拍摄蜂窝并记录与蜂窝相关的各种变量,例如:蜂窝中有多少花蜜,有多少蜜蜂,有多少花粉,有多少幼虫,有多少密封的巢(容纳蜂蜜),有多少蛹。由于计算这些的任务过于繁重,研究人员记录了这些近似值(例如,5%的蜂巢有蜂蜜)。
Types of honeycomb cells. Glossy cells contain nectar. Sealed cells contain honey.
对于黑客马拉松,我们选择使用对象检测来解决计数问题。通过对象检测,我们可以自动识别不同类型的细胞并进行计数,以便研究人员可以记录这些细胞的准确数据。
对象检测是深度学习和计算机视觉的一个分支,涉及自动识别图像中感兴趣的项目,并确定围绕它们的矩形边界框(bbox)的坐标。然后可以使用坐标画出边界框。
Object detection example. (source)
数据注释
One of the images taken by the researchers. We did not annotate it completely due to time constraints.
在机器学习(ML)和深度学习(DL)中,对数据进行注释或标记意味着将其标记为属于某一类。当使用对象检测时,这意味着确定围绕图像中感兴趣区域的矩形的坐标(或者在使用注释工具时围绕感兴趣区域绘制矩形),并保存矩形的坐标和该区域所属的类。我们希望 ML 或 DL 模型从带注释/标签的数据中学习,并用这些注释/标签预测新数据。
One labeled cell.
我们在研究人员使用名为 labelImg 的开源标记工具拍摄的蜂窝图像中标记了花蜜和空细胞,该工具将注释保存到 XML 文件中。
LabelImg’s GUI.
我们只关注 nectar 和 empty 类,以便在实现解决方案之前不要花太多时间来注释数据,还因为它们是我们注释的蜂巢中最丰富的单元。最理想的情况是,我们应该标记所有的种类:密封的、空的、花蜜的、蜜蜂的、蛹的、幼虫的和花粉的。
Preview of one of the label XML files. Bounding box coordinates are within the tags.
注:在黑客马拉松期间,我们将花蜜细胞标记为蜂蜜细胞。
Oops.
我们认为光泽细胞有蜂蜜,而事实上它们有花蜜(从观看这个视频中发现)。文章的其余部分将保持原来的约定,因为它反映了黑客马拉松结束之日的现实。通过在注释(XML)文件中找到单词“honey”的所有实例并用单词“nectar”替换它们,可以很容易地修复注释。我们将在未来的工作中实现这一修复。
数据预处理
阅读标签
使用 labelImg 创建的注释以 XML 格式保存在名为labeled_data
的文件夹中。我们开发代码是为了以一种更容易使用的格式存储这些标签。
我们的代码遍历了labeled_data
中的所有 XML 文件,其中保存了图像及其 XML 注释。然后,它使用库BeautifulSoup
访问 XML 中每个边界框的相关信息。然后,它将信息存储到 dataframe 中,其中每行对应一个边界框,列的标题为:image(图像的相对路径)、label(边界框的标记)、xmin(左 x 点)、xmax(右 x 点)、ymin(顶部 y 点)和 ymax(底部 y 点)。
Diagram example of an image with a bbox. xmin, xmax, ymin and ymax define all edges of the bbox.
Code for reading in annotations from all XML files in labeled_data folder and storing them into a dataframe.
在黑客马拉松中,我们标注了 562 个“蜂蜜”单元格和 619 个“空”单元格。
用于读取使用 labelImg 创建的标签的代码存在于 GitHub repo 的mlTool
文件夹中,作为一个名为read_labels.ipynb
的 Jupyter 笔记本。
从大图像创建小图像
研究人员拍摄的图像非常大,分辨率很高。例如,之前显示的照片的宽度为 4608 像素,高度为 3456 像素。
Original image properties.
对象检测模型最适合大量较小的图像,因此我们决定从大图像中创建较小的 300 x 300 像素图像。让我们称这些更小的图像为“块”。我们只保留包含至少一个边界框的块。
Separate each image into many 300 x 300 px images, and adjust bounding boxes to the new dimensions.
可以从原始图像中提取 192 个大小为 300×300 px 的块。90 个组块包含边界框。
目标检测模型
由于时间限制,我们使用了 Azure 的自定义视觉 AI 来快速训练一个对象检测模型。
我们无法确定在在线 GUI 中导入已经标记的数据来训练对象检测模型的方法,所以我们选择使用 API。
以自定义视觉可接受的格式保存标签
自定义视觉人工智能期望标记的数据是一种特殊的格式。左上点和右下点在 0 和 1 之间标准化。
This is the one thing that made us not start training the model before the hackathon finish time.
这些是文档中的规范:
这些区域在标准化坐标中指定边界框,并且坐标以左、上、宽、高的顺序给出。(来源)
Code for extracting chunks from images present in the labeled_data folder.
左相当于 xmin,右相当于 ymin。我们通过减去 xmax 和 xmin 得到一个边界框的宽度,通过减去 ymax 和 ymin 得到它的高度。
将标签保存为 custom vision AI 接受的格式的代码存在于 GitHub repo 的mlTool
文件夹中,作为标题为image_chunks.ipynb
的 Jupyter 笔记本。
创建对象检测模型
我们使用定制的 vision AI Python 库azure-cognitiveservices-vision-customvision
连接到 Python 中的 API,创建一个项目,并将图像和边界框导入到项目中。我们通过访问在线界面验证了它的工作原理。
We added 619 bounding boxes belonging to the ‘empty’ class, and 562 belonging to the ‘honey’ class.
Custom Vision projects home screen.
View when Honeycomb project is selected.
模型在 90 幅大小为 300×300 像素的图像上进行训练。58 幅图像包含“空”类的边界框,53 幅图像包含“蜜”类的边界框(一幅图像可能包含不止一个类)。
Image count per tag in Custom Vision AI interface.
在点击和查看图像时,我们能够在视觉上确认我们正确地将标签转换为 Custom Vision AI 接受的格式。
View when an image is clicked. Bounding boxes and their labels are present.
这方面的代码存在于一个名为Azure_Model.ipynb
的 Jupyter 笔记本的mlTool
文件夹中的存储库中。
火车
为了训练我们在 Custom Vision AI 中设置的模型,我们访问了 customvision.ai ,登录到我们的帐户,进入我们使用 API 创建的项目,并选择右上角名为“训练”的绿色按钮。
Click the green button titled “Train” in order to train the model.
出现一个模式窗口,要求在两种培训类型之间进行选择:快速培训和高级培训。选择高级培训可以选择培训的小时数,但会产生费用。我们选择了快速培训,以避免收费并快速获得结果。
We chose fast training due to time constraints. For better performance, choose advanced training.
快速训练花了几分钟。
表演
默认情况下,Custom Vision AI 将基于 50%的概率阈值和 30%的重叠阈值显示模型的性能。这些值可以用可用的刻度盘来修改。
By default, probability threshold is 50% and overlap threshold is 30%. Changing these changes the scores.
使用默认的概率和重叠阈值,该模型的准确率为 48%,召回率为 31.9%,mAP 为 43.0%。
Visual representation of scores. Precision is 48%, recall is 31.9%, and mAP is 43%.
自定义视觉人工智能还提供了每类得分的性能。在“蜂蜜”类上,该模型的准确率为 55.9%,召回率为 24.8%,平均准确率为 50.6%。在“空”类上,它的准确率为 41.2%,召回率为 48.3%,A.P .为 35.5%。
Performance per tag. Performed better in identifying the ‘honey’ class.
“蜂蜜”类比“空”类得分更高。“空的”班级比“有蜂蜜的”班级的回忆率高。
这些分数可以通过更多的数据(图像和注释)和更多的迭代来提高。重要的是下次不要让感兴趣的区域未被标记,因为模型可能会因此而混淆(它会认为这是背景噪声)。
预测
我们对用于训练的蜂巢图像的一个微小片段进行了截图,并通过点击自定义视觉 AI 界面右上角的按钮“快速测试”并上传截图来对其进行预测。
We clicked on the button titled ‘Quick Test’ in order to get the predictions for an image.
Predictions with default probability threshold of 15%.
该模型仅预测了图像中总共约 27 个蜂房中的 4 个蜂房,其概率超过 15%。
我们将概率阈值降低到 2%,出现了更多正确的边界框。正确边界框的数量从 4 个增加到 19 个。这意味着大多数预测的蜂蜜细胞的概率低于 15%。
Predictions at 2% probability threshold. Most predicted honey cells have probabilities below 15%.
结果和结论
对于黑客马拉松,我们注释了一小部分数据,处理了注释和图像,以自定义视觉 AI 接受的格式保存了注释,并使用自定义视觉 AI 训练了一个对象检测模型
通过 90 幅大小为 300 x 300 像素的图像,619 个用于“空”类的边界框,562 个用于“蜜”类的边界框,并且在仅一次迭代的训练之后,Custom Vision AI 实现了 48%的精度、31.9%的召回率和 43%的 mAP。对于“蜂蜜”类,定制视觉人工智能的准确率为 55.9%,召回率为 24.8%,平均准确率(A.P .)为 50.6%。对于“空”类,Custom Vision AI 的准确率为 41.2%,召回率为 48.3%,A.P .为 35.5%。
由于训练数据包含未标记的感兴趣对象,模型的性能受到阻碍。这些分数将随着完全标记的数据、附加数据和更多的训练迭代而提高。
由于蜂窝的性质,大多数边界框在某种程度上重叠。重叠的边界框可能会导致性能问题。我们应该考虑语义分割。
经验教训
如果你希望为黑客马拉松实现深度学习解决方案,我建议你在电脑上安装/设置一个对象检测架构,并在黑客马拉松之前学习如何使用它,这样一旦你标记了数据,你就可以快速使用它。SSD、 MaskRCNN 和 YOLO 是很好的开源对象检测架构。
至少一名团队成员应该专注于编写图像处理的代码,而其他成员则专注于为概念证明添加足够的数据。应该为数据注释定义一个时间限制和最小数量的注释,以免将注释时间延长太久。
蜂窝图像应该被裁剪成较小的片段,以确保所有单元都被标记,从而防止混淆模型。
后续步骤
为了继续并改进该项目,我们需要:
- 标记更多图像
- 可能选择不同的标注工具
- 考虑语义分割
- 选择不同的对象检测架构,如 SSD 或 MaskRCNN
- 更多时代的训练模型
- 评估模型性能并加以改进
- 创建一个 API 来服务模型
- 将模型整合到研究人员可以轻松使用的应用程序中
- 合并数据库
感谢您的阅读!