如何构建一个简单的网络爬虫
一步一步的指南刮出最好的全球大学排名
三年前,我在新加坡 NTU 大学的机构统计部门担任学生助理。
我被要求通过手动从网站上复制并粘贴到 excel 表格中来获得最佳全球大学排名。我很沮丧,因为我的眼睛累了,因为我长时间连续看着电脑屏幕。因此,我在想是否有更好的方法来做这件事。
那时,我在谷歌上搜索自动化,找到了答案——网络抓取。从那以后,我设法创建了 100 多个网络爬虫,这是我的第一个网络爬虫,我想和大家分享一下。
之前我做的是用 requests 加 BeautifulSoup 来完成任务。然而,三年后,当我再次访问同一个网站时,我发现有一种方法可以获取 JSON 数据,而且速度更快。
如果你正在考虑自动化你的无聊和重复的任务,请向我保证你会读到最后。您将学习如何创建网络爬虫,以便您可以专注于更多增值任务。
在这篇文章中,我想分享我如何建立一个简单的爬虫来抓取 usnews.com 大学的排名。
检查网站
当你想抓取网站时,首先要做的是检查 web 元素。为什么我们需要这样做?
这实际上是为了发现是否存在更可靠的方法来获取数据或获得更清晰的数据。对于前者,我这次没有深究去挖掘 API。然而,我确实找到了提取更干净数据的方法,这样我就可以减少数据清理时间。
如果您不知道如何检查 web 元素,您只需导航到网页的任意位置,右键单击,单击检查,然后单击网络选项卡。之后,刷新您的页面,您应该会看到一个网络活动列表逐一出现。让我们来看看我在上面的截图中使用光标选择的特定活动(即"search?region=africa&..."
)。
之后,请参考上面截图中的紫色框,突出显示浏览器将向其发送请求的 URL,以便获得要呈现给你的数据。
嗯,我们可以通过向那个 URL 发送请求来模仿浏览器的行为,并获得我们需要的数据,对吗?但是在这之前,为什么我选择调用请求网址而不是原来的网站网址?
让我们点击预览选项卡,你会注意到我们需要的所有信息,包括大学排名、地址、**国家等…**都在蓝色框中突出显示的结果字段中。
这就是我们勉强通过网址的原因。URL 返回的数据是一种非常好的格式——JSON 格式。
上面的截图显示了今天和 3 年前的代码对比。3 年前,当我还是一个网络抓取新手时,我只是使用请求、漂亮的页面、大量的 XPATH 和繁重的数据清理过程来获取我需要的数据。然而,如果你比较我今天写的代码,我只需要使用 httpx 来获取数据,不需要清理数据。
供您参考,httpx 是建立在请求之上的,但是它支持额外的功能,比如提供异步 API,使用 httpx,您可以发送 HTTP/2 请求。更完整的对比,你可以参考这篇文章。
请求网址:
https://www . us news . com/education/best-global-university/search?区域=非洲&主题=农业科学&格式=json
所以现在,让我们来关注一下如上图所示我们将要使用的链接。您会注意到,您可以更改 region 和 subject 的值,因为它们是 URL 的参数。(关于 URL 参数的更多信息,这里的是一个很好的阅读)但是,请注意,它们的值仅限于网站提供的区域和主题。
例如,您可以将地区=非洲改为地区=亚洲或者科目=农业科学改为**科目=化学。**如果你有兴趣了解支持的地区和题材有哪些,可以访问我的回购了解一下。
在了解了如何查询这个 URL 来获取你需要的数据之后,剩下的部分就是对于一个特定的地区和主题的组合,你需要查询多少个页面。
那么,我们就以这个网址为例,将网址复制粘贴到你的浏览器中并回车,然后用command+f
搜索关键词“last_page”,你会发现类似如下的截图。
*请注意,我已经安装了一个 chrome 扩展,它可以帮助我将普通数据美化成 JSON 格式。这就是为什么您可以看到我的浏览器中显示的数据打印得很好。
恭喜你,你成功找到了如上所示的 last_page 变量。现在,剩下的唯一过程是,如果 last_page 大于 1,如何转到下一页并获取数据。
下面是我如何找到导航到第 2 页的方法。以这个环节为例。
首先,点选页码 2,然后检视右边的面板。请注意紫色的框,您会注意到在请求 URL 中添加了 page=2。这意味着您只需要将&page={page_number}
附加到原始请求 URL 上,以便在不同的页面间导航。
现在,您已经对如何创建一个 web scraper 来从网站获取数据有了一个整体的想法。
如果你想看完整的 Python 代码,请随意访问这里的。
最终想法
非常感谢你一直读到最后。
以下是我想让你看完这篇文章后得到的收获。
- 知道有许多不同的方法可以从网站上抓取数据,例如获取 JSON 格式的数据链接。
- 花一些时间检查网站,如果你设法找到检索数据的 API,这可以节省你很多时间。
我之所以将我三年前写的代码和我今天写的代码进行比较,是为了让你知道如何通过不断的练习来提高你的网页抓取和编码技能。
努力吧,结果一定会来的。——低伟鸿
如果你有任何问题或想法要问或补充,欢迎在下面评论!
关于作者
Low 魏宏是 Shopee 的数据科学家。他的经验更多地涉及抓取网站,创建数据管道,以及实施机器学习模型来解决业务问题。
他提供爬行服务,可以为你提供你需要的准确和干净的数据。你可以访问这个网站查看他的作品集,也可以联系他获取的抓取服务。
在媒体上阅读低纬鸿的作品。数据科学家|网络抓取服务:https://www.thedataknight.com/.每…
medium.com](https://medium.com/@lowweihong?source=post_page-----6bef8cb1477a----------------------)
如何构建智能搜索引擎(下)
用 Python 创建智能搜索服务
作者所有图片
在本系列的第一篇文章中,我们只用了几行代码就构建了一个搜索引擎,该引擎采用了当今许多大型企业搜索引擎中使用的 BM25 算法。
在这篇文章中,我们想超越这一点,并创造一个真正智能的搜索引擎。这篇文章将描述这样做的过程,并提供在任何数据集上实现这一点的模板代码。
但是我们所说的“聪明”是什么意思呢?我们将它定义为一个能够:
- 向用户返回相关结果,即使他们没有在这些结果中搜索特定的单词。
- 注意位置;了解英国邮政编码和英国城镇的地理关系。
- 能够扩展到更大的数据集(我们将迁移到一个更大的数据集,而不是上一个示例中的 212k 记录,但是我们需要能够扩展到更大的数据)。
- 比我们上次的实施快几个数量级,甚至在搜索大型数据集时也是如此。
- 以明智的方式处理拼写错误、打字错误和以前“看不见”的单词。
为了实现这一点,我们需要结合多种技术:
- 快速文本单词向量。我们将在我们的数据集上训练一个模型来创建单词的向量表示(更多信息在这里)。
- BM25。我们仍将使用这种算法来支持我们的搜索,但我们需要将它应用到我们的词向量结果中。
- 使用轻量级和高效的非度量空间库(NMSLIB) 超快速搜索我们的结果。
这将看起来像下面这样:
我们将在本帖中创建的管道概述
本文将逐一介绍这些领域,并描述如何将它们结合起来创建一个智能搜索引擎。
1。设置;预处理和标记文本
创建搜索引擎的第一步是将我们的文档分割成单独的单词或“记号”。spaCy 库使这变得非常简单和快速。提醒一下,我们在本文中使用的例子与上一篇文章中的例子相同。它包含发布在 Contracts Finder 平台上的英国公共部门合同通知。但是,出于本练习的目的,我们增加了数据集的大小(现在是 212k 记录,以前只有 50k)。除此之外,我们还将位置数据引入了我们的数据集。在进行任何处理之前,我们使用的数据框如下所示:
数据集的一些示例记录(总共 212k 记录)
我们将在搜索引擎中使用的列是“文本”列,它是每个通知的自由文本和位置字段的合并。
我们可以使用 spaCy 库,对这个列进行清理和标记。以下代码通过使用 spaCy 管道来实现这一点,这使得处理尽可能高效,并且还允许我们仅选择我们想要使用的令牌化器引擎的部分(再次确保该过程尽可能快):
上面的代码将我们的文档分割成一个令牌列表,同时执行一些基本的清理操作,删除标点符号、空白并将文本转换为小写。在 Colab 笔记本上运行,每秒钟可以处理 1800 多个通知。
2。创建单词向量;建立一个快速文本模型
为什么字矢?为什么不是伯特/GPT-3/[最新的 SOTA NLP 模型]?
自从引入像 BERT 这样复杂的变压器模型以来,单词向量模型似乎已经过时了。然而,由于以下原因,它们今天仍然具有相关性:
- 与 transformer 模型相比,它们在创建可伸缩服务的所有重要方面(模型大小、训练时间、推理速度)都是“轻量级”的。
- 由于上述原因,他们可以在特定领域的文本上从头开始训练。除此之外,它们可以在相对较小的数据集上被训练(即,数千个文档,而不是通常用于训练变压器模型的数百万个文档)。
- 它们更容易解释,因为单词向量将保持一致,并且不会根据周围文本的上下文而改变(这既是优点也是缺点,稍后将详细介绍)。
除此之外,使用 Gensim 库实现它们非常简单。这里我们正在构建一个快速文本模型:
回顾绩效:
现在我们已经训练好了我们的模型,让我们看看它的表现如何。
fastText 在捕获语料库中单词之间的关系方面的有效性一直让我感到惊讶。有几个例子可以很好地说明这一点:
与‘M4’最相似的单词:
ft_model.wv.most_similar("m4", topn=20, restrict_vocab=5000)
与“m4”最相似的单词。该模型理解与英国高速公路命名的联系。分数越高,相似性越大
这真的令人震惊,该模型已经清楚地了解到 M4 与英国高速公路相关,并理解其他大型英国高速公路与此类似(M1、M5、M3、M60)。
它还了解到,LRN 也密切相关(这代表当地的道路网络)我甚至不知道这一点!
“9AT”标记看起来很奇怪,但是快速搜索就会发现这是英国高速公路的邮政编码。
在单词向量模型中包含邮政编码和位置信息是经过深思熟虑的设计选择。基本原理是该模型将理解英国邮政编码和位置如何相互关联。让我们来测试一下:
与‘约克郡’最相似的单词:
与“Yorkshire”最接近的单词的相似性得分越高,表示相似性越大
该模型了解到约克郡是英国的一个地区(位于西北部)以及其中的主要城镇。它还理解该区域和其子区域之间的关系;这里的“骑马”指的是约克郡境内的北/东/西骑马。但是邮政编码呢?
与‘RG9’最相似的词:
RG9 是英国境内与亨利镇相关的邮政编码。这是一个棘手的例子,因为亨利是一个很小的城镇,RG 邮政编码也用于附近其他更大的城镇(如雷丁)。该模型能够正确地将此邮政编码与 Henley 关联起来吗?
该模型知道 RG9 邮政编码与亨利镇相关
它出色地通过了!Henley 是最相似的单词,其他结果也高度相关,代表邻近的城镇和邮政编码。
显然,我们的词向量模型表现良好,在下一步中,我们将使用 BM25 算法来增强我们的词嵌入。
3。将 BM25 应用于单词向量
现在我们有了单词向量,我们需要找到一种方法,在搜索引擎中为每个文档组合这些向量。
最简单的方法是对每个文档的单词向量进行平均(这是可行的),但是已经证明将单词向量与 BM25 算法相结合可以产生更高质量的搜索结果[1]
下面是 BM25 的简单回顾,但是请回顾我的第一篇文章,以获得更多关于其内部工作的细节:
BM25 内部运作的回顾
虽然这看起来很复杂,但用我们的单词 vectors 实现它实际上非常简单,只需要几行代码(就像本文中的所有其他步骤一样!)
将我们的单词向量转换成使用 BM25 加权的文档向量
这个输出将为我们的搜索引擎中的每个文档提供一个向量。
4。使用 NMSLIB 创建超快速搜索索引
我们现在已经有了数据集中每个文档的向量列表。我们还可以使用上面概述的技术为用户的搜索查询创建一个向量。
但是我们如何根据这个搜索查询返回相关的结果呢?我们需要能够找到与我们的搜索向量最近的向量。鉴于我们的向量中有大量的维度(100 ),这是我们的方法可能开始失败的地方。搜索引擎需要很快,在超过 20 万条记录的数据集中搜索超过 100 个维度是非常耗费资源的。
NMS lib:
令人欣慰的是,这是计算机科学中一个相当普遍的挑战,并且已经有解决方案来大规模加速相似性搜索问题。NMSLIB 是最快的解决方案之一2。使用这个库,我们可以创建一个搜索索引,这将比使用暴力方法查找相似向量快几个数量级:
将所有这些放在一起;更智能的搜索引擎:
现在我们有了搜索索引,只需创建一个搜索向量并从索引中返回最接近的匹配项:
使用与我们上一篇文章中相同的查询 【洪水防御】 ,我们现在得到以下结果(前 5 名):
Searched 212,447 records in 0.0005 seconds:Q3172 PROPERTY FLOOD MITIGATION SCHEME WASH GREEN, WIRKSWORTH, DERBYSHIRE SUPPLY AND INSTALL CERTIFIED FLOOD PROTECTION PRODUCTS INCLUDING FLOOD DOORS, FLOOD BARRIERS, AIR BRICKS AND OTHER WORKS IDENTIFIED IN THE PROPERTY LEVEL FLOOD PROTECTION SURVEY REPORTS, AS WELL AS SKIMMER PUMPS AND HOSES. Matlock DE4 3AG WHIMPLE FLOOD DEFENCE IMPROVEMENTS CONSULTANCY SERVICES FOR PREPARATION OF CONTRACT FOR CONSTRUCTION OF FLOOD ALLEVIATION SCHEME. Sidmouth EX10 8HL FLOOD RISK ASSESSMENT FLOOD RISK ASSESSMENT Woolwich SE186HQ PAPERMILL DYKE FLOOD DEFENCE WALL CONSTRUCTION OF FLOOD DEFENCE Doncaster DN1 3BU MVDC - JS - LEVEL 2 STRATEGIC FLOOD RISK ASSESSMENT LEVEL 2 STRATEGIC FLOOD RISK ASSESSMENT TO SUPPORT PREPARATION OF THE FUTURE MOLE VALLEY LOCAL PLAN Surrey RH4 1SJ
一些伟大的成果。此外,搜索在 0.0005 秒内完成。这比我们之前的搜索引擎快了 122 倍,尽管数据集的大小超过了 4 倍。
同样值得强调的是,许多结果虽然高度相关,但并不包含“防御”一词。使用单词向量的方法意味着现在不再需要精确的单词匹配来返回相关的结果。
考虑到地理信息也应该在搜索索引中进行编码,让我们尝试搜索一个在特定区域授予的合同。为此,我们将使用 NR2 邮政编码进行搜索,以查找诺里奇的通知: “审计服务 NR2”。 以下是前 3 名的结果:
Searched 212,447 records in 0.0004 secondsPROVISION OF EXTERNAL AUDIT SERVICES THE CONTRACT IS A SINGLE LOT FOR THE PROVISION OF EXTERNAL AUDIT SERVICES. Norwich NR4 6TJGB-NORWICH: EXTERNAL AUDIT ANNUAL AUDIT OF TRUST FINANCIAL & QUALITY ACCOUNTS AND ANNUAL REPORT. Norwich NR6 5BEGB-NORWICH: 18-022 - INTERNAL AUDIT SERVICES BROADLAND HOUSING GROUP WISHES TO ENTER INTO A CONTRACT FOR INTERNAL AUDITING. Norwich NR1 1HU
有用!返回诺里奇的内部和外部审计服务的所有结果,请注意,即使我们使用 NR2 邮政编码进行搜索,它也知道这与其他诺里奇的 NR4、NR6 和 NR1 邮政编码类似……非常聪明!
最后,让我们输入一个错别字,看看它是否还能以智能的方式处理这个问题。 《审计服务在诺维奇》:
Searched 212447 records in 0.0005 secondsPROVISION OF EXTERNAL AUDIT SERVICES THE CONTRACT IS A SINGLE LOT FOR THE PROVISION OF EXTERNAL AUDIT SERVICES. Norwich NR4 6TJGB-NORWICH: EXTERNAL AUDIT ANNUAL AUDIT OF TRUST FINANCIAL & QUALITY ACCOUNTS AND ANNUAL REPORT. Norwich NR6 5BEGB-NORWICH: 18-022 - INTERNAL AUDIT SERVICES BROADLAND HOUSING GROUP WISHES TO ENTER INTO A CONTRACT FOR INTERNAL AUDITING. Norwich NR1 1HU 0.13
同样的结果再次出现,尽管城镇名称拼写错误。
总之:
在这篇文章中,我们已经看到了如何将单词向量与 BM25 结合起来,并通过快速相似性搜索索引对其进行增压,从而创建一个智能的、可伸缩的和高性能的搜索引擎。
尽管如此,考虑这是否对最终用户有益总是很重要的。例如,我们可能会发现用户更喜欢简单的关键字搜索,因为他们可以很容易地解释结果。这也凸显了创造“更智能”服务的最大风险之一;它们通常会变成:
- 不太容易预测,
- 学习搜索数据中存在的偏见;
- 由于复杂性的增加,调试会更加困难。
由于这些原因,它们需要大量的测试,以确保一旦投入生产,它们就能按预期运行。
随着搜索变得越来越复杂,随着时间的推移,我们无疑会看到更复杂的解决方案出现。看到这一领域如此快速的发展是一件好事,但同样重要的是要记住,通常最简单的问题解决方案是最好的。
笔记本包含数据和代码
用 Python 创建智能搜索引擎的代码
colab.research.google.com](https://colab.research.google.com/drive/10ZrZaLBmhEqKSSEoe_cos783m2junUdr?usp=sharing)
参考文献:
[1]搜索引擎中的词嵌入,质量评测https://ad-publications . cs . uni-freiburg . de/themes/学士 _ 埃内科 _Pinzolas_2017.pdf
2相似性搜索库的基准https://github.com/erikbern/ann-benchmarks
链接到本系列的第一部分:
用几行代码在 Python 中创建健壮的全文搜索
towardsdatascience.com](/how-to-build-a-search-engine-9f8ffa405eac)
一如既往,非常感谢 TDS 编辑团队!
如何构建一个 Streamlit UI 来分析葡萄酒、虹膜和乳腺癌数据集上的不同分类器
让我们使用 Streamlit 和 sklearn 构建一个 web 应用程序
作者的屏幕截图
在本教程中,我们将使用三个数据集(虹膜、乳腺癌、葡萄酒)
我们将使用 3 种不同的模型(KNN,SVM,随机森林)进行分类,并让用户能够设置一些参数。
安装并导入必要的库
设置虚拟环境
pip install virtualenv /* Install virtual environment */
virtualenv venv /* Create a virtual environment */
venv/Scripts/activate /* Activate the virtual environment */
安装库
安装库之前,请确保您的虚拟环境已激活
pip install streamlit, seaborn, scikit-learn
导入库
import streamlit as st
from sklearn.datasets import load_wine, load_breast_cancer, load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
我们导入 streamlit,来自 sklearn 的数据集,来自 sklearn 的各种模型,制作我们的地块和熊猫所需的库。
助手功能
函数来获取数据集
def return_data(dataset):
if dataset == 'Wine':
data = load_wine()
elif dataset == 'Iris':
data = load_iris()
else:
data = load_breast_cancer()
df = pd.DataFrame(data.data, columns=data.feature_names , index=None)
df['Type'] = data.target
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, random_state=1, test_size=0.2)
return X_train, X_test, y_train, y_test,df,data.target_names
- 该函数接受一个字符串,该字符串包含用户选择的数据集的名称
- 它加载相关的数据集
- 我们创建了一个可以在 UI 中显示的数据帧
- 我们使用 sklearn 的 train_test_split() 来创建训练集和测试集
- 该函数返回训练集、测试集、数据帧和目标类
函数返回模型
我们将使用 streamlit 的滑块组件从用户那里获取参数输入。
st.sidebar.slider(label = ’ ',min_value = 1,max_value = 100) 在侧边栏中创建一个滑块。
def getClassifier(classifier):
if classifier == 'SVM':
c = st.sidebar.slider(label='Chose value of C' , min_value=0.0001, max_value=10.0)
model = SVC(C=c)
elif classifier == 'KNN':
neighbors = st.sidebar.slider(label='Chose Number of Neighbors',min_value=1,max_value=20)
model = KNeighborsClassifier(n_neighbors = neighbors)
else:
max_depth = st.sidebar.slider('max_depth', 2, 10)
n_estimators = st.sidebar.slider('n_estimators', 1, 100)
model = RandomForestClassifier(max_depth = max_depth , n_estimators= n_estimators,random_state= 1)
return model
- 和前面的函数一样,这个函数接受一个参数,这个参数是一个包含模型名称的字符串。
- 基于所选的模型,我们要求用户给出参数值。
- 对于 SVM,我们将 C 参数作为用户的输入
- 对于 KNN,我们在进行预测时考虑模型的最近邻居的数量
- 对于随机森林,我们取决策树的数目和决策树的最大深度
- 然后,我们创建模型的实例并返回模型
PCA 的功能
def getPCA(df):
pca = PCA(n_components=3)
result = pca.fit_transform(df.loc[:,df.columns != 'Type'])
df['pca-1'] = result[:, 0]
df['pca-2'] = result[:, 1]
df['pca-3'] = result[:, 2]
return df
我们用的是 sklearn 的 PCA。我们将 3 个组件添加到数据帧中并返回它。
构建用户界面
作者截图
# Title
st.title("Classifiers in Action")
# Description
st.text("Chose a Dataset and a Classifier in the sidebar. Input your values and get a prediction")
#sidebar
sideBar = st.sidebar
dataset = sideBar.selectbox('Which Dataset do you want to use?',('Wine' , 'Breast Cancer' , 'Iris'))
classifier = sideBar.selectbox('Which Classifier do you want to use?',('SVM' , 'KNN' , 'Random Forest'))
我们使用 streamlit 的 selectbox 组件创建一个下拉菜单,供用户选择数据集和模型
# Get Data
X_train, X_test, y_train, y_test, df , classes= return_data(dataset)
st.dataframe(df.sample(n = 5 , random_state = 1))
st.subheader("Classes")
for idx, value in enumerate(classes):
st.text('{}: {}'.format(idx , value))
- 我们使用助手函数来获取数据
- 我们使用 streamlit 的 dataframe 组件来显示数据集的一个示例
- 我们还使用 helper 函数返回的最后一个变量来显示这些类
我们将使用 seaborn 和 matplotlib 在 2d 和 3d 中可视化 PCA。
streamlit 的 pyplot 组件接受一个图形作为参数,并在 UI 中显示该图形。
作者截图
# 2-D PCA
df = getPCA(df)
fig = plt.figure(figsize=(16,10))
sns.scatterplot(
x="pca-1", y="pca-2",
hue="Type",
palette=sns.color_palette("hls", len(classes)),
data=df,
legend="full"
)
plt.xlabel('PCA One')
plt.ylabel('PCA Two')
plt.title("2-D PCA Visualization")
st.pyplot(fig)
作者截图
#3-D PCA
fig2 = plt.figure(figsize=(16,10)).gca(projection='3d')
fig2.scatter(
xs=df["pca-1"],
ys=df["pca-2"],
zs=df["pca-3"],
c=df["Type"],
)
fig2.set_xlabel('pca-one')
fig2.set_ylabel('pca-two')
fig2.set_zlabel('pca-three')
st.pyplot(fig2.get_figure())
最后,我们将对模型进行训练,并得到训练、测试的准确率分数。
# Train Model
model = getClassifier(classifier)
model.fit(X_train, y_train)
test_score = round(model.score(X_test, y_test), 2)
train_score = round(model.score(X_train, y_train), 2)
st.subheader('Train Score: {}'.format(train_score))
st.subheader('Test Score: {}'.format(test_score))
你已经成功地建立了一个项目,你可以展示你的投资组合👏 👏 👏
我最近用 WordPress 创建了一个博客,如果你能看看的话,我会很高兴的😃
[## Python 项目教程-使用这些 Python 项目教程改进您的简历/作品集。
使用 Streamlit 共享部署您的机器学习 Web 应用程序在我以前的文章中,我谈到过构建一个…
realpythonproject.com](https://realpythonproject.com/)
在 LinkedIn 上与我联系
[## Rahul baner JEE——产品工程实习生——EY | LinkedIn
查看 Rahul Banerjee 在世界上最大的职业社区 LinkedIn 上的个人资料。拉胡尔有 4 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/rahulbanerjee2699/)
在 Twitter 上与我联系
使用 TensorFlow.js 通用句子编码器的文本相似性
从学者到构建相似句子分组网络应用的旅程
一些布赖顿的沐浴盒比其他的更相似。“给我看看那些红色的”[照片由 me 拍摄]
你想知道搜索引擎是如何理解你的查询并检索相关结果的吗?聊天机器人如何从你的问题中提取你的意图,并提供最恰当的回应?
在这个故事中,我将详细介绍构建文本相似性分析 web 应用程序所需的每个部分,包括:
- 单词嵌入
- 句子嵌入
- 余弦相似性
- 构建文本相似性分析网络应用
- 结果分析
试试文本相似性分析网络应用,在下面的评论中让我知道它是如何为你工作的!
什么是单词嵌入?
单词嵌入启用 知识表示,其中向量表示单词。这提高了神经网络从文本数据集学习的能力。
在单词嵌入成为自然语言处理的事实标准之前,处理单词的一种常见方法是使用一次性矢量化。每个单词代表向量空间中的一列,每个句子是一个由个 1和个 0组成的向量。个表示单词在句子中的存在。
一键矢量化[摘自文本编码:综述
结果,这导致了一个巨大而稀疏的表示,因为零比一多得多。当一个词汇表中有很多单词时,它会创建一个很大的单词向量。这可能会成为机器学习算法的一个问题。
一键矢量化也无法捕捉单词的含义。比如“饮料”和“饮料”,虽然这是两个不同的词,但是定义却差不多。
通过单词嵌入,语义相似的单词具有相似的向量表示。因此,当出现类似“我想点一杯饮料”或“一杯饮料”的短语时,点餐系统可以以同样的方式解释该请求。
过去的单词嵌入
早在 2003 年, Yoshua Bengio 等人引入了语言模型概念。那篇论文的重点是学习单词的表示,这允许模型预测下一个单词。
这篇论文是至关重要的,并导致了单词嵌入的发展和发现。约舒厄与杰弗里·辛顿和扬·勒昆一起获得了图灵奖。
将单词的特征向量序列输入到单词的条件概率分布中,以预测下一个单词[图片取自论文
2008 年,罗南和杰森研究了一个可以学习识别相似单词的神经网络。他们的发现为自然语言处理开辟了许多可能性。下表显示了单词列表以及十个最相似的单词。
左图:给定输入句子的神经网络结构,输出类别概率。右表:选出的 5 个单词和 10 个最相似的单词。[资料来源于论文
2013 年, Tomas Mikolov 等人介绍了从拥有数十亿单词的数据集学习高质量的单词向量。他们将它命名为 *Word2Vec,*它包含了词汇中的数百万个单词。
Word2Vec 从此开始流行。如今,单词嵌入层在所有流行的深度学习框架中。
单词嵌入示例
在谷歌预训练的 Word2Vec 模型上,他们从谷歌新闻数据集中训练了大约 1000 亿个单词。单词“猫”与“猫”、“狗”、“老鼠”、“宠物”的意思最接近。
“猫这个词在几何上更接近于“猫”、“狗”、“老鼠”、“宠物”。【摘自嵌入式投影仪
单词嵌入还设法识别单词之间的关系。一个经典的例子是单词之间的性别角色关系。比如,“男之于“女”就好比“王”之于“后”。
从手套无监督学习算法学习到的单词之间的有趣关系[ 图像源
深入挖掘单词嵌入
加利纳·奥莱尼克在描述单词嵌入的动机方面做得非常出色。从一键编码和 TF-IDF 到手套和庞加莱。
[## 单词嵌入:探索、解释和利用(带 Python 代码)
单词嵌入讨论是每个自然语言处理科学家都在谈论的话题
towardsdatascience.com](/word-embeddings-exploration-explanation-and-exploitation-with-code-in-python-5dac99d5d795)
这里有一篇由迪潘然(DJ)萨卡尔撰写的 29 分钟的关于各种语言模型的综合文章。他涵盖了 Word2Vec、GloVe 和 FastText 如果你打算研究单词嵌入,一定要看看这个。
[## 文本数据深度学习方法的直观实践方法— Word2Vec、GloVe 和 FastText
驯服非结构化文本数据的更新、高级策略
towardsdatascience.com](/understanding-feature-engineering-part-4-deep-learning-methods-for-text-data-96c44370bbfa)
项目的 Word 嵌入资源
TensorFlow 在这个 Colab 笔记本中提供了一个关于单词嵌入和代码的教程。您可以尝试使用这些代码,并用它来训练您在数据集上的单词嵌入。这个绝对可以帮你入门。
对于喜欢动画的人来说,在嵌入投影仪上有一个很酷的嵌入可视化。每个点代表一个单词,你可以在三维空间中可视化语义相似的单词。
一个截屏嵌入投影仪。你看到的每个点代表一个单词。
我们有单词向量来代表单词的意思;句子怎么样?
什么是通用句子编码器?
像单词嵌入一样,通用句子编码器是一个通用的句子嵌入模型,它将文本转换成有语义意义的固定长度向量表示。
通用句子编码器将文本编码成高维向量[取自 TensorFlow Hub
由通用句子编码器产生的这些向量捕获丰富的语义信息。我们可以将它用于各种自然语言处理任务,训练分类器,如分类和文本相似性分析。
Google 有两个通用的句子编码器模型。其中一个基于变压器架构,另一个基于深度平均网络。
Transformer ,句子嵌入为每个单词创建上下文感知的表示,以生成句子嵌入。它是为更高的精度而设计的,但是编码需要更多的内存和计算时间。这对于情感分类是有用的,在情感分类中,像‘not’这样的词可以改变意思,并且能够处理像‘不错’这样的双重否定。
深度平均网络,单词的嵌入首先一起平均,然后通过一个前馈深度神经网络产生句子嵌入。不幸的是,通过平均向量,我们在这个过程中失去了句子的上下文和句子中的单词序列。它是为了速度和效率而设计的,牺牲了一些准确性(尤其是在讽刺和双重否定上)。一个很好的主题分类模型,将长文章分类。
如果句子可以得到相同的回答,那么它们在语义上是相似的。[摘自论文
杨等人介绍了一种利用会话数据学习句子表征的方法。
比如,“你多大了?、你多大了?”,两个问题语义相似,一个聊天机器人可以回复同一个答案“我 20 岁”。
“你好吗?”以及“你多大了?”即使有相同的单词也有 33%的相似度[ demo
相比之下,虽然"你好吗?、你多大了?“包含相同的单词,两个句子有不同的意思。聊天机器人必须理解问题并给出适当的回答。
这是一张显示三个句子相似度的热图“你多大了?、你多大了?、你好吗?”。
“你好吗?**你多大了?“即使具有相同的单词,也具有低相似度分数。
Logeswaran 等人引入了一个从无标签数据中学习句子表征的框架。在本文中,现有方法中使用的解码器(橙色框)被从一组候选句子(绿色框)中选择目标句子的分类器所取代;它提高了问答系统的性能。
用从一组候选句子中选择目标句子的分类器代替先前方法中的解码器[摘自论文
深入了解通用句子编码器
Dipanjan (DJ) Sarkar 解释了各种嵌入模型的发展。如果您热衷于构建文本分类器,他的文章详细介绍了在电影评论数据集上执行情感分析的每个步骤。
揭秘通用句子编码器指南
towardsdatascience.com](/deep-transfer-learning-for-natural-language-processing-text-classification-with-universal-1a2c69e5baa9)
如果你好奇探索其他语言模型, Pratik Bhavsar 对比了 BERT、ELMo、USE、Siamese、InferSent 等各种语言模型的性能。学会选择正确的答案会改善你的结果。
文本的主特征工程
medium.com](https://medium.com/modern-nlp/on-variety-of-encoding-text-8b7623969d1e)
为您的项目提供通用句子编码器资源
TensorFlow 提供了关于通用语句编码器的教程,预训练模型和笔记本。如果你正在考虑构建自己的文本分类器,一定要看看这个。
有了每个句子的语义向量,我们如何衡量句子之间的相似性呢?
余弦相似度是什么?
余弦相似度是通过计算两个向量之间的余弦角来衡量相似度。如果两个向量相似,则它们之间的角度较小,余弦相似值更接近 1。
给定两个向量 A 和 B ,余弦相似度 cos(θ)使用点积和幅度来表示【来自维基百科
在这里,我们将句子输入通用句子编码器,它返回给我们句子嵌入向量。
有了向量,我们就可以得到向量之间的余弦相似性。对于每一个句子对, A 和 B ,我们可以计算出 A 和 B 向量的余弦相似度。
语义相似度是衡量两个文本表达相同意思的程度。[摘自 TensorFlow Hub
我们可以确定将句子分组在一起最小阈值。当相似性得分在 0 到 1 之间时,也许我们可以选择 0.5,在中间点。这意味着任何相似度大于 0.5 的句子都将被聚集在一起。
深入挖掘衡量相似性的方法
Euge Inzaugarat 介绍了六种度量向量间相似性的方法。每种方法都适用于特定的环境,因此了解它们就像了解您的数据科学工具箱一样。
这完全取决于你的观点
towardsdatascience.com](/how-to-measure-distances-in-machine-learning-13a396aa34ce)
文本相似性分析网络应用的构建模块
照片由 Ryan Quintal 在 Unsplash 上拍摄
在这个项目中,我将使用这些库:
- TensorFlow.js
- 通用句子编码器
- 有角的
TensorFlow.js
TensorFlow.js 是 Google 建立的一个框架,支持 JavaScript 的机器学习。我们可以开发机器学习模型,并将它们部署在网络浏览器和 Node.js 中。
因为我喜欢开发 web 应用程序,所以当 TensorFlow.js 在 2018 年发布时,我非常高兴。
很容易上手,我们可以用 npm 安装 TensorFlow.js。
$ npm install @tensorflow/tfjs
简单线性回归模型的示例如下。
import * as tf from '@tensorflow/tfjs';const model = tf.sequential();
model.add(tf.layers.dense({units: 1, inputShape: [1]}));model.compile({loss: 'meanSquaredError', optimizer: 'sgd'});const xs = tf.tensor2d([1, 2, 3, 4], [4, 1]);
const ys = tf.tensor2d([1, 3, 5, 7], [4, 1]);model.fit(xs, ys, {epochs: 10}).then(() => {
model.predict(tf.tensor2d([5], [1, 1])).print();
});
通用句子编码器
我将使用的是来自 TensorFlow.js 的通用句子编码器包。我们可以使用 npm 安装通用语句编码器。
$ npm install @tensorflow-models/universal-sentence-encoder
这个例子展示了我们如何使用通用句子编码器从每个句子中提取嵌入内容。
import * as use from '@tensorflow-models/universal-sentence-encoder';use.load().then(model => {
const sentences = [
'Hello.',
'How are you?'
];
model.embed(sentences).then(embeddings => {
embeddings.print(true /* verbose */);
});
});
有角的
Angular 是 Google 为创建动态单页应用而构建的 web 应用框架。
对于这个项目,我使用的是 Angular 8.0。我喜欢在 Angular 的模型-视图-控制器设计模式的基础上构建。从 Angular 的第一个版本开始,我就一直在使用它,在我的大部分 web 开发中也是如此。但由于他们每半年推出一次主要版本,感觉我的工作会变得过时(也许?我不知道)。React 是一个流行的 UI 框架,所以也许有一天我会改用 React。谁知道呢?
余弦相似性
创建一个函数,使用余弦相似性公式计算两个向量的相似性。
similarity(a, b) {
var magnitudeA = Math.sqrt(this.dot(a, a));
var magnitudeB = Math.sqrt(this.dot(b, b));
if (magnitudeA && magnitudeB)
return this.dot(a, b) / (magnitudeA * magnitudeB);
else return false
}
计算每个句子对的相似性得分的另一个函数如下。
cosine_similarity_matrix(matrix){
let cosine_similarity_matrix = [];
for(let i=0;i<matrix.length;i++){
let row = [];
for(let j=0;j<i;j++){
row.push(cosine_similarity_matrix[j][i]);
}
row.push(1);
for(let j=(i+1);j<matrix.length;j++){
row.push(this.similarity(matrix[i],matrix[j]));
}
cosine_similarity_matrix.push(row);
}
return cosine_similarity_matrix;
}
将一切结合在一起
照片由 Amélie Mourichon 在 Unsplash 上拍摄
我已经介绍了这个项目所需的所有主要组件。现在我们只需要把它们像乐高积木一样堆叠起来,打包并部署到 Github。
瞧啊。我们得到了一个用于现场演示的 web 应用程序。
输入语义相似的句子列表[ 演示
我们有一个句子列表,这些将被输入到通用句子编码器中。它将输出每个句子的嵌入。然后我们计算每个句子之间的相似度。
结果
这些是我们将测试我们的通用句子编码器的句子。目的是把意思相似的句子组合在一起。我挑了几个比较难的案例,让我们看看它的表现如何。
明天会下雪吗?
最近许多飓风袭击了美国
全球变暖是真的一天一个苹果,医生远离我吃草莓有益健康
你多大了?
你多大了?你好吗?约翰尼被狗咬了
猫吃了老鼠
老鼠吃了猫
这张热图显示了每个句子与其他句子的相似程度。绿色越亮,表示相似度越接近 1,这意味着句子之间越相似。
12 个句子对的语义相似度[ demo
我们可以调整该值来确定一个最小相似度阈值,以便将句子组合在一起。这些是以大于 0.5 的相似性值分组在一起的句子。
最近许多飓风袭击了美国全球变暖是真的
每天一个苹果,医生远离我吃草莓是健康的
第三组
你多大了?你多大了?第四组
狗咬了约翰尼
约翰尼咬了狗第五组
猫吃了老鼠
老鼠吃了猫
我们的 web 应用程序出色地识别出了“ Group 1 ”是天气相关问题。即使两个句子没有任何重叠的单词。
它成功地识别出“飓风”和“全球变暖”与天气有关,但不知何故未能将“雪”归入这一类别。
可惜,“强尼咬了狗”和“狗咬了强尼”有着 87%的相似度。可怜的约翰尼,我不知道哪个更好。
同样,对于“猫吃了老鼠”和“老鼠吃了猫”,我希望这两个向量有相反的相似性。
感谢您到目前为止的阅读!
再一次,试试文本相似性分析网络应用,在下面的评论中让我知道它是如何为你工作的!
如果您想构建类似的东西,请查看 web 应用程序的代码。
我构建的其他机器学习网络应用
因为我喜欢构建 web 应用程序,所以我开发了这些 web 应用程序来展示 web 上的机器学习能力。一定要跟随我的媒体(广告歌),因为我会建立更多这样的。
使用 TensorFlow.js 进行时间序列预测。
从在线 API 中提取股票价格,并使用 RNN 和 LSTM 以及 TensorFlow.js 进行预测(包括演示和代码)
towardsdatascience.com](/time-series-forecasting-with-tensorflow-js-1efd48ff2201)
一个学习玩井字游戏的强化代理。
代理使用价值函数学习井字游戏的强化学习算法——带网络演示
towardsdatascience.com](/reinforcement-learning-value-function-57b04e911152)
参考
[1] Bengio,Yoshua,等.一种神经概率语言模型。(2003)
2科洛波特、罗南和杰森·韦斯顿。"自然语言处理的统一架构:具有多任务学习的深度神经网络。(2008)
[3] Mikolov,Tomas,等.向量空间中单词表示的高效估计。(2013)
[4] Cer,Daniel 等.通用语句编码器。(2018)
[5]杨,,等.从会话中学习语义文本相似度.(2018)
[6] Logeswaran、Lajanugen 和 Honglak Lee。"学习句子表征的高效框架。(2018)
如何用 RNN 和喀拉斯建立翻译管道
你有没有想过一台计算机是如何能够如此快速地学习多种语言的?按照这个逐步指南创建您的第一个翻译模型。
巴别塔,老彼得·布鲁盖尔的画
在上一篇文章中,我们看到了 FFNN 在增益上下文方面的局限性。对于这些情况,更好的方法是 RNN,它可以管理上下文并生成状态,以便更好地理解跨时间步长的数据。在本文中,我们将通过使用 keras 创建翻译模型来实践我们所学的内容。
更准确地说,我们将开发一种多对多类型的 RNN,也称为序列对序列或 Seq2Seq。更高级的 Seq2Seq 结构包括编码器-解码器或注意力模型。我们将构建的模型如下所示。
作者图片
输入层接收英语句子,每个单词是一个时间步长。然后,隐藏层(RNN)计算每个时间步长的状态,该状态将用于下一个时间步长,输出将用于密集层。
数据清理
样本数据可以在manythings.org下载,来自 Tatoeba 。它由你需要的语言中的句子对组成。在我们的例子中,我们将使用西班牙语-英语对。
我们需要做的第一件事是导入库:
然后我们将读取文件并解析数据。
为了不让计算机处理大量的线对并保持例子的简洁,我们将只处理少量的数据。让我们来看看几双。
从上一个片段打印。作者图片
句子包含有大写字母和标点符号的单词,所以让我们清除它。在本文中,我们将看到每个步骤的代码片段和示例。最后,所有内容都将合并到一个文件中。
它将打印“我今天要冲浪”。
标记器
机器学习模型不能阅读单词,只能阅读数字,为了给模型提供数据,我们需要将单词转换成数字。我们从 Keras 导入 Tokenizer 并应用两种方法。首先我们实例化这个类,然后用完整的文本调用方法 fit_on_texts 。得益于此,我们将创建一个字典,我们将一个单词映射到一个索引,每个唯一的单词都有一个唯一的索引。我们已经创建了一个名为 text_examples 的例子列表,我们有 3 个句子。这三个句子是我们的完整数据集,所以当我们调用这个方法时,它会为每个单词创建一个新的索引。让我们看看通过打印 word_index.items() 我们创建了什么。
从上一个片段打印。作者图片
每个单词都有自己的索引,例如单词“beach”即使出现了两次,也只是在索引为 5 的情况下创建一次,对于索引为 3 的“to”也是如此。我们已经创建了映射,但是我们还没有将句子转换成那些索引。为此,我们需要调用方法 texts_to_sequences ,它的作用不再是创建映射,而是应用它。它采用句子“我今天将冲浪”,并将“我”更改为 1,“将”更改为 2,“冲浪”更改为 6,“今天”更改为 7。因此,句子“我今天将冲浪”变成了[1,2,6,7]。
从上一个片段打印。作者图片
看起来很奇怪,电脑读数字更舒服,因为它失去了单词的所有意义。当两个单词是同义词时会发生什么?**一个模型如何知道 234 和 67 是否有相似的含义?它来了嵌入,不是把一个单词映射到一个索引,而是把一个单词映射到一个向量。**计算这些向量可以保留单词的意思,并创建一个空间表示。当我们表示这些向量时,具有相似意义的单词也将具有相似的坐标。这种技术被称为嵌入,将在下一篇文章中讨论。
我们创建一个函数来返回向量和映射。
让我们把我们到目前为止所看到的应用到句子对而不是例子中,并探索结果。
从上一个片段打印
西班牙语有 7198 个独特的单词,而英语只有 3736 个。此外,另一个区别是句子的最大长度,而西班牙语由 12 个单词组成,英语的最大长度是 6 个单词。等等,到目前为止,我们已经看到,两个句子应该是相同的长度,以适应 RNN 结构,我们如何处理不同的长度?
填料
为了使所有的句子长度相同,我们使用 Keras 的 T2 填充序列。这个类的作用非常简单,对于那些长度小于最大长度的句子,它会加一个 0。回到我们的列表 text_examples ,最大长度是 8,而第一个句子的长度是 4,那么当我们应用填充时,我们有[1 2 6 7 0 0 0 0]。添加了 4 个零,使其长度为 8。其他两个句子的长度已经是 8,则不应用任何更改。
从上一个片段打印。作者图片
一个更直观的例子如下。对于时间步长为 8 的 RNN,我们要翻译句子“鸟儿歌唱”它变成了“los pájaros están cantando”
RNN 结构,作者图片
我们将在构建模型时参考这个图像。我们将相同的代码应用于西班牙英语对。
我们准备好了,句子已经被清理了,它们也变成了向量,由于填充,它们的长度是一样的。我们的训练数据准备好了。
模型创建
我们需要定义的第一层是输入层,图像’ RNN 结构’中的蓝色层。在 Keras 中,RNN 的输入形状是 3D(批量大小,时间步长,特征)。输入层有两个元素(时间步长,特征),我们已经知道时间步长,从我们的最大句子长度 12,和特征是在时间步长的观察数量,在我们的例子中只有一个。然后 batch_size 被定义为对象模型的方法 fit 的一个参数,Keras 假设它为 1 或更大。
我们添加的第二层是 RNN,更准确地说,在这种情况下,我们处理的是长短期记忆(LSTM)。需要注意的一个重要参数是return _ sequences,默认设置为 False,该图层的输出将只是最后一个时间步的矢量,如下图所示。
情感分析(多对一)。RNN 在最后一步输出矢量,作者的图片
我们不需要在每个时间步获得输出,只需要在最后获得输出,这样密集层就可以做出预测。但是在我们的例子中,正如我们在图像’ RNN 结构 ’ 中看到的,在每个时间步都有一个预测,所以 RNN 层在最后不会输出一个矢量,而是在每个时间步输出一个矢量。
多对多。当 return_sequences=True 时,RNN 层在每个时间步长输出一个矢量
在这个解释之后是介绍时间分布的时候了。尽管概念上非常简单,但当你第一次面对它时,它会引起一些混乱。我们刚刚在 RNN 层中设置了 return_sequences=True ,所以我们在每一步都有一个输出向量。那我们该怎么办?**应用一个密集层,**所以最后用一个激活层我们可以做一个预测。这个致密层是什么样子的?
输入层接收一个形状为 256 的矢量,它相当于 LSTM 层中的 256 个单位。输出层有一个 7198 的形状,代表我们词汇中唯一西班牙语单词的总数( spanish_vocab )。预测字将是 7198 中已经激活的单元。因此,如果最终向量除了单元 324 之外都是零,其中我们有一个 1,我们将索引 324 映射到标记化器,并获得翻译的单词。这是在每个孤立的时间步完成的。
作者图片
我们刚刚看到了如何应用密集层,但是时间分布有什么用呢?这仅仅意味着我们在每个时间步应用先前解释的层。因为我们使用 return_sequence=True ,RNN 层在每个时间步输出一个矢量,因此我们需要在每个时间步应用相同的密集层。如果我们放大模型的输出层,它看起来如下。
作者图片
总而言之,为了创建模型,我们应用以下代码。
该模式的总结是:
最后,我们训练我们的模型。参数尚未优化,本文的目的是了解和创建管道。
一旦我们完成训练,让我们做一些预测。正如我们之前看到的,模型在每个时间步长的输出是形状 7198 的向量,其中激活的单元是预测的单词,因此例如,如果在我们的第一次预测之后,我们有输出 324,我们需要将索引映射到西班牙语单词。下面的函数将为我们完成这项工作。
为了检查最终的预测,我们使用下面的代码。
你可以在 下面的链接 中找到一个 jupyter 笔记本,里面有完整的代码
摘要
在这篇文章中,我们把学到的关于 RNN 的概念付诸实践。
我们让 c 学习数据,创建了一个索引来将每个单词映射到一个向量,并将所有的句子转换成那些向量,这要感谢分词器。然后我们使用 pad_sequences 让所有的句子都有一样的长度。
为了创建模型,我们定义了输入形状,我们用 return_sequences=True 创建了一个 LSTM 层,然后由于 TimeDistributed ,在每个时间步应用了一个密集层。
在下一篇文章中,我们将详细阐述模型架构,以创建一个更好的执行翻译。从理论的角度来看,我们将讨论不同类型的 RNN 建筑,如 LSTM,并分析之前介绍的术语,如嵌入。
如何使用 Memgraph、Cypher 和 Python 构建旅行规划应用程序
了解如何利用广度优先搜索(BFS)和 Dijkstra 算法,用 Memgraph、Cypher 和 Python 构建一个简单的旅行规划应用程序。
Christopher Czermak 在 Unsplash 上拍摄的照片
介绍
背包旅行是一种在预算范围内探索世界的好方法。凭借其良好的交通网络、经济实惠的住宿条件和数以千计的景点,欧洲是背包客的完美旅行目的地。
在本教程中,您将学习如何利用 Memgraph、Cypher 和 Python 构建一个简单的旅行规划应用程序。您将学习如何使用图形数据库,以及如何使用 Python 语言驱动程序查询它。您还将学习如何利用流行的图形算法,包括广度优先搜索(BFS) 和 Dijkstra 的算法。
先决条件
要跟进,您需要以下内容:
- 运行 Linux 的机器。
- Memgraph 的本地安装。您可以参考Memgraph 文档。
- Python 内存图客户端。
- Python 编程语言的基础知识。在本教程中,我们将使用 Python 3。
- Cypher 查询语言的基础知识。
步骤 2 —构建数据模型
首先,我们必须定义我们将用来构建您的应用程序的数据模型。用于图形分析的最常见的数据模型之一是标签属性图(LPG)模型。这个模型被定义为一组顶点(即节点)和边(即关系)以及它们的属性和标签。
在本教程中,您将使用欧洲背包客指数(2018)数据集,该数据集包含位于 36 个欧洲国家的 56 个城市的信息。您的模型将包含两个类型为City
和Country
的顶点,三个类型为Inside
、CloseTo
和Borders
的边,以及一些属性,如名称、排名、当地货币等。
既然已经定义了数据模型,下一步就是将数据导入 Memgraph。
步骤 3—使用 Cypher 和 Python 将数据导入 Memgraph
为了将数据导入 Memgraph,您将通过 Python 客户端pymgclient
使用 Cypher 查询。
pymgclient 是 Python 编程语言的 Memgraph 数据库适配器,符合 PEP 249 描述的 DB-API 2.0 规范。
Cypher 是一种声明式查询语言,被许多人认为是处理属性图数据库的行业标准。
在开始之前,你必须安装pymgclient
。这将允许您连接到 Memgraph 并运行您的查询。为此,我们将运行以下命令:
pip install pymgclient
现在我们已经安装了pymgclient
,我们准备导入它并连接到 Memgraph 数据库。为此,我们将使用以下 Python 代码:
import mgclient# Connect to the database
connection = mgclient.connect(
host='127.0.0.1',
port=7687,
sslmode=mgclient.MG_SSLMODE_REQUIRE)
connection.autocommit = True
既然我们的 python 客户机已经连接到 Memgraph,我们就可以开始运行查询了。为了加速数据导入,我们在City
和Country
顶点的id
属性上创建索引。这将有助于 Memgraph 在创建边时快速找到城市和国家。注意,我们可以在另一个属性上创建索引,比如name
,我们将获得相同的结果。
[注意]数据库索引实质上是创建数据库中某些数据的冗余副本,以提高索引数据的搜索效率。然而,这是以额外的存储空间和更多的写入为代价的,因此决定索引什么和不索引什么是一个重要的决定。
为了创建索引,我们将执行以下查询:
connection.cursor().execute("""
CREATE INDEX ON :City (id)
""")
connection.cursor().execute("""
CREATE INDEX ON :Country (id)
""")
既然我们的索引已经创建,我们将开始导入我们的,从国家开始。为此,我们将运行下面的查询。
正如您所看到的,这个查询使用了CREATE
子句来创建一个带有一个标签Country
和两个属性id
和name
的顶点。
接下来,我们将添加City
节点及其所有属性,如当地货币、餐饮的平均价格、交通费用等。为此,我们将使用下面的查询。
如您所见,该查询使用相同的CREATE
子句创建标签为City
的节点,以及 14 个不同的属性。
现在我们已经创建了图表中的所有节点,我们准备开始添加边。为此,我们将运行下面的查询。
在这个查询中,我们首先使用MATCH
子句获取两个City
节点,我们将在这两个节点之间创建一条边,然后使用CREATE
子句创建一条标签为CloseTo
的边和一个值为True
或False
的属性eu_border
。
恭喜你。现在,您已经将所有数据集导入 Memgraph。现在,您可以开始执行查询和算法了。
步骤 4—用 Python 运行简单的密码查询
首先,我们可以运行一些简单的查询,比如获得旅馆最便宜的前 10 个城市。为此,您将运行以下查询:
cursor = connection.cursor()
cursor.execute("""
MATCH (n:City)
RETURN n.name, n.cheapest_hostel, n.cost_per_night_USD, n.hostel_url
ORDER BY n.cost_per_night_USD LIMIT 10
""")
print(cursor.fetchall())
该查询匹配所有标签为City
的顶点,并返回城市名称、最便宜的旅馆名称、该旅馆每晚的费用以及旅馆 URL。然后,它根据最便宜的旅馆每晚的费用对结果进行排名,并返回前 10 个结果。
如果你去意大利旅游,想知道哪些城市适合背包客,哪家旅社对他们来说最便宜,该怎么办?要获得答案,您将运行以下查询:
cursor = connection.cursor()
cursor.execute("""
MATCH (c:City)-[:Inside]->(:Country {name: "Italy"})
RETURN c.name, c.cheapest_hostel, c.total_USD
ORDER BY c.total_USD;
""")
print(cursor.fetchall())
如您所见,该查询类似于我们之前使用的查询,但是我们没有匹配所有城市,而是只匹配通过类型Inside
的边缘连接到意大利的城市
步骤 5—使用广度优先搜索算法查找和过滤路径
尽管这些查询给了我们一些有趣的见解和结果,但它们并不是图数据库特别感兴趣的东西。当我们开始询问涉及遍历任意数量的边的更复杂的问题时,图数据库变得很有价值。这些类型的寻路查询对于传统数据库来说可能会有问题,因为它们通常需要多个连接。
假设您想要从西班牙旅行到俄罗斯,但是您想要选择穿过最少边界的路线。这就是像广度优先搜索(BSF)这样的图算法派上用场的地方。要获得答案,您将使用以下查询:
cursor = connection.cursor()
cursor.execute("""
MATCH p = (n:Country {name: "Spain"})
-[r:Borders * bfs]-
(m:Country {name: "Russia"})
UNWIND (nodes(p)) AS rows
RETURN rows.name;
""")
print(cursor.fetchall())
该查询使用类型为Borders
的边评估西班牙和俄罗斯之间的所有可能路径,计算每条路径的过境次数,并返回过境次数最少的路径。
如果您想根据特定标准过滤路径,该怎么办?假设您想从布拉迪斯拉发开车到马德里,并想计划您的路线以尽量减少停靠站的数量。你也想只去那些把欧元作为当地货币的国家,因为那是你唯一剩下的货币。为此,您将使用以下查询:
cursor = connection.cursor()
cursor.execute("""
MATCH p = (:City {name: "Bratislava"})
-[:CloseTo * bfs (e, v | v.local_currency = "Euro")]-
(:City {name: "Madrid"})
UNWIND (nodes(p)) AS rows
RETURN rows.name;
""")
print(cursor.fetchall())
如您所见,我们向查询(e, v | v.local_currency = "Euro")
添加了一个特殊的语法。这被称为过滤λ函数。过滤器λ取一个边符号e
和一个顶点符号v
,并通过返回 true 或 false(或 Null)来决定该边和顶点对在广度优先扩展中是否应该被认为是有效的。在本例中,如果城市顶点v
的类型v.local_currency
的属性值等于 Euro,lambda 函数返回 true。一旦确定了最佳路径,查询将返回该路径上的城市列表,并且UNWIND
子句将该列表解包到单独的行中。
步骤 6-使用 Dijkstra 算法寻找最短路径
到目前为止,您使用了广度优先搜索算法来查找穿过最少边数的路径。但是,如果你想找到一条最短的路径,并考虑到沿途每个城市的住宿价格,那该怎么办呢?换句话说,如果你想找到最短最便宜的路径呢?这是广度优先搜索算法达到极限的地方。只有当所有的边和顶点都没有权重或者具有相同的权重时,BFS 才会准确地计算最短路径。
当涉及到在图中寻找边和顶点不具有相同权重的最短路径时,您将需要使用 Dijkstra 算法。
例如,假设你想从布鲁塞尔旅行到雅典,而你的预算很紧。为了找到最便宜的路线,您可以使用以下查询:
cursor = connection.cursor()
cursor.execute("""
MATCH p = (:City {name: "Brussels"})
-[:CloseTo * wShortest (e, v | v.cost_per_night_USD) total_cost]-
(:City {name: "Athens"})
WITH extract(city in nodes(p) | city.name) AS trip, total_cost
RETURN trip, total_cost;
""")
print(cursor.fetchall())
如您所见,语法几乎与我们的 BFS 查询相同。我们使用加权最短路径wShortest
并指定cost_per_night
属性类型作为我们的权重。权重 lambda 表示使用给定边v.cost_per_night_USD
扩展到指定顶点的成本,total cost
符号计算行程成本。extract 函数仅用于显示城市名称。要获得完整的城市信息,您应该返回nodes(p)
。
结论
你有它!您刚刚学习了如何使用图形数据库、Cypher 和 Python 构建一个简单的旅行路线规划应用程序。您还了解了如何使用广度优先搜索算法和 Dijkstra 算法在复杂的互联数据网络中导航,并使用强大而灵活的 lambda 函数过滤结果。
如何建立扭曲线性回归模型
优化和机器学习
我们使用模块峰值引擎对数据进行单调转换
普通最小二乘法 (OLS)将线性回归模型拟合到数据集,以便在误差正态分布的假设下最大化似然性。当误差项分解成独立同分布分量的和时,正态性自然会出现,但对许多问题来说,这种假设是不现实的。
例如,考虑一个目标值表示百分比的回归数据集。对于一个给定的特征向量,OLS 可能会预测一个这样的分布
这里怎么了?分布显示目标值可能大于 100%。当目标空间是有界的时,关于末端附近预测的正态分布误差是没有意义的。
如果不满足正态假设,我们有时可以通过转换目标空间来解决问题。假设 f 是单调递增函数。让 X 和 y 表示特征矩阵和目标向量。放 z = f( y *)。*虽然 OLS 可能无法很好地模拟原始数据集,但它有可能适合 X 和 z
我们如何找到这样一个函数 f ?这就是翘曲帮助我们的地方。弯曲从一族参数化单调函数开始。该族足够一般,可以近似任意变换。如果 ψ 表示弯曲函数的参数向量,那么我们使用优化器来调整 ψ 以最大化训练数据的可能性。
Peak-engines 是一个 python 模块,用于构建这种扭曲的线性回归模型。我们将使用它为一个示例数据集构建一个模型,并展示我们如何改进 OLS。
我们将使用的示例是波士顿房屋数据集,其任务是根据社会经济和地理属性预测房屋的中值。像百分比一样,房屋价值是有限的(你不会找到免费的房屋),因此有理由认为目标空间可以从扭曲中受益。
首先,让我们设置数据集。Boston housing 附带 sklearn,便于组装。
在我们建立扭曲线性回归模型之前,我们需要安装峰值引擎
pip install peak-engines
然后我们可以拟合一个扭曲的线性回归模型。
Peak-engines 在 OLS 之前搜索要应用的扭曲函数空间,直到找到最大化可能性的变换。为了可视化转换的样子,我们将它绘制在目标值的范围内。
翘曲函数在较低的住房价值更陡峭。它在目标空间的低端展开点,在高端压缩点。
为了比较扭曲线性回归和 OLS,让我们运行留一交叉验证。对于每个数据点,我们形成一个去除了该点的训练数据集和一个只包含该点的测试数据集。我们拟合 OLS 和扭曲的线性回归模型,并衡量对数似然性能。
结果显示验证点更有可能是扭曲线性回归。我们将查看一些随机预测示例,以更好地理解为什么它做得更好。
扭曲函数导致概率密度函数在较低的目标值处逐渐变小,从而允许概率质量被重新分配到值更可能出现的区域。
完整示例的代码可从这里获得。
摘要
当面临回归问题时,OLS 通常是我们寻求的第一个模型,然而许多数据集并不满足其对正态分布误差的强假设。扭曲线性回归建立在 OLS 的基础上,通过引入额外的变换步骤来扭曲目标空间,以校正误差中的非正态性。它保留了 OLS 模型的许多简单性,但更通用,并经常导致更好的性能。
参考
[1]: E .斯尼尔森,CE Rasmussen,Z . Ghahramani。 【扭曲高斯】突起 。神经信息处理系统的进展 16,337–344
使用 Keras 和 OpenCV 的自定义对象检测
建立一个可以在给定的图像或画面中识别武器的系统
武器探测系统(原图)
我最近完成了一个令我非常自豪的项目,我想我应该分享它,以防其他人有兴趣实现类似于他们特定需求的东西。在我开始本教程之前,我想对 PyImageSearch 的创建者 Adrian Rosebrock 博士表示深深的感谢。我是一个自学成才的程序员,所以没有他的资源,这个项目的大部分是不可能的。他是一个典型的男人- 我非常感激他在自己的网站上提供的资源。如果你想学习先进的深度学习技术,但发现教科书和研究论文很枯燥,我强烈建议访问上面链接的他的网站。
在大多数与武器分类相关的项目中,我只能找到最多 100-200 张图片的数据集。这提出了一个问题,因为根据我的经验,很难用这么少的图像得到一个工作模型。为了收集图像,我用我的树莓皮刮了一下IMFDB.com——一个枪支爱好者张贴照片的网站,照片中的模型枪出现在一部电影的画面或剪辑中。如果你访问网站,这一点会更清楚。要访问我用过的图片,你可以访问我的 Google Drive 。在这个 zip 文件中,您将找到所有在这个项目中使用的图像和相应的。边界框的 xml 文件。如果你需要一个大数据集的边界框,我强烈推荐 ScaleOps。AI ,一家专注于机器学习算法数据标注的公司。目前,我有来自 IMFDB 网站的 120,000 张图片,但是由于时间和资金的限制,我只使用了大约 5000 张。
现在,让我们来看看逻辑。这个项目的架构遵循这个网站上显示的逻辑。虽然我们在这里实现了这个逻辑,但是在许多领域它是不同的,因此它可以用于我们的特定问题——检测武器。该项目使用 6 个基本步骤:
- 使用 OpenCV 选择性搜索分割构建数据集
- 建立一个 CNN 来检测你想要分类的物体(在我们的例子中,0 =没有武器,1 =手枪,2 =步枪)
- 在根据选择性搜索分割构建的图像上训练模型
- 为新图像创建边界框时,通过选择性搜索分割运行图像,然后抓取图片的每一部分。
- 通过算法运行图像的每一部分,每当算法预测到你要寻找的对象时,用边界框标记位置
- 如果标记了多个边界框,应用非最大值抑制,只包括具有高置信度/感兴趣区域的框(这部分我还在弄清楚…你将在下面看到我的问题)
下面是展示算法如何工作的 gif。对于给定的图像,每个方块将被输入到神经网络中。如果一个正方形被预测为阳性(手枪或步枪),我们将标记我们输入到原始图像的区域。
滑动窗口方法:对象检测(作者图片)
如果你想看这个项目的完整代码,请访问我的 GitHub Repo,在那里我会更深入地解释这些步骤。
我上面链接的数据包含了很多文件夹,我需要解释一下,以便理解正在发生的事情。解压文件夹后,这些文件和文件夹对项目很重要:AR、FinalImages、Labels、Pistol、Stock_AR、Stock_Pistol 和 PATHS.csv。所以对于 AR 文件夹,你会发现里面有突击步枪的图片。在标签文件夹中,您会看到。类文件夹中所有图像的 xml 标签。最后,PATHS.csv 将指向算法中使用的每一张图像。出于本教程的目的,这些是您唯一需要担心的文件夹/文件:
- 最终图像/NoWeapon
- 最终图像/手枪
- 最终图像/步枪
这些文件夹中的图像制作方式如下。
- 对于每个有边界框的图像,提取边界框并将其放入相应的类文件夹中。因此,对于一个人拿着手枪的图像,手枪周围的边界框将变成正的,而边界框以外的每个部分都将变成负的(没有武器)
- 在下图中,想象一个包围左侧图像的边界框。在提取边界框内的像素(右边的图像)后,我们将该图像放入另一个文件夹(FinalImages/Pistol),同时将边界框周围的所有空白区域放入 NoWeapons 文件夹。
- 虽然右边的图像看起来像是左边图像的调整版本,但它实际上是一个分段图像。想象在左边的枪周围有一个边界框。右边的图像是仅仅是边界框,没有别的(移除框外的所有东西)。这种技术被称为感兴趣区域(ROI)。
ROI 提取(图片由作者提供)
在收集数据集(可以在 Separated/FinalImages 中找到)后,我们需要为我们的算法使用这些文件,我们需要以这样的方式准备它,我们有一个 RGB 值列表和相应的标签(0=没有武器,1 =手枪,2 =步枪)
如果您运行上面的代码,在当前目录的之外有一个单独的文件夹*,您会看到一个 tqdm 窗口,显示它正在加载图像。该过程完成后,您应该会看到以下内容:*
(图片由作者提供)
现在是神经网络的时候了。在下面的代码中,该函数将返回一个给定维度大小的模型。如果您在上面的代码中注意到,照片的尺寸被调整为(150,150,3)。如果您希望使用不同的尺寸,请确保您更改了上面的变量 dim,以及下面函数中的 DIM
上面返回的模型将具有如下所示的体系结构:
CNN 架构(图片由作者提供)
一旦我们有了训练集和测试集,我们需要做的就是把它放到我们的模型中。运行下面的代码将开始训练过程。
如果运行代码时没有任何错误,您应该会看到如下窗口:
我想指出的是,我将历元设置为 1000,但提前停止将防止算法过度拟合,因此它不应运行超过 30–50 个历元。模型完成后,您应该会在您的目录中看到一个名为 ModelWeights.h5 的. h5 文件。该文件是模型生成的权重,因此将它们加载到模型中会在模型开始溢出之前加载模型。
模型的准确性(图片由作者提供)
每个班级的 ROC(图片由作者提供)
考虑到一个平衡的数据集,这个精确度是相当不错的。查看 ROC 曲线,我们还可以假设每个类别下的面积非常接近 1,这是一个非常好的分类。
现在是物体探测的时间了!以下逻辑用于创建边界框:
- 输入视频中的图像或帧并检索基本预测
- 应用选择性搜索分割来创建数百或数千个包围盒命题
- 通过训练的算法运行每个边界框,并检索预测与基本预测相同的位置(在步骤 1 中)
- 检索到算法预测与基本预测相同的位置后,在算法运行的位置上标记一个边界框
- 如果选择了多个边界框,则应用非最大值抑制来抑制除一个框之外的所有框,留下具有最高概率和最佳感兴趣区域(ROI)的框
- 注意:非最大值抑制仍在进行中。在某些情况下,它只能检测枪的特征,而不是整个枪本身(见下面的模型比较)。
在运行上面的代码之前,创建一个文件夹 Tests,从互联网上下载任何图像,并将该图像命名为您想要预测的类。运行上面的代码将搜索 Tests 文件夹中的每张图片,并使用上面构建的 CNN 通过我们的对象检测算法运行该图片。
我测试的图像如下:
基础图像
运行上面的代码后,这些是算法输出的预测。
模型结果(图片由作者提供)[点击此处查看大图]
正如您在上面看到的,非极大值抑制并不完美,但在某种意义上确实有效。我这里的问题是,有多个具有 100%置信度的边界框,所以很难选择哪一个是最好的*。*此外,当帧中没有武器时(绵羊图像),该算法无法检测非武器。
使用上面实现的逻辑,这里的是我将代码应用到视频的一个很酷的视觉效果。
演示武器检测(视频由作者提供)
基于上面的例子,我们看到这个算法离完美还有一段距离。这没什么,因为我们仍然创建了一个非常酷的模型,只用了 5000 张图片。就像我之前说的,我从 IMFDB.com 收集了总共 120,000 张图片,所以随着我们在培训期间传递更多的图片,这只会变得更好。
石灰:特征提取
构建和测试神经网络的一个困难部分是,它的工作方式基本上是一个黑盒,这意味着你不明白为什么权重是它们的样子,或者算法在图像中使用什么来进行预测。使用 LIME ,我们可以更好地理解我们的算法是如何执行的,以及图片中的哪些内容对于预测是重要的。
石灰预测
运行上面的代码将创建一个如下所示的图像:
石灰(图片由作者提供)
绿色区域是算法认为“重要”的区域,而红色区域则相反。考虑到我们希望算法检测枪的特征,而不是手或图像的其他部分,我们在上面看到的是好的。
现在我们可以说我们创造了我们自己的有意识的生物…是时候面对现实了。与现有的工具相比,我们制作的模型微不足道。这使我转而学习…我们看到了一些很酷的结果。为了这个教程,我不会把代码放在这里,但是你可以在我的 GitHub Repo 上找到它
Mobilenet
- 在下面的例子中,mobilenet 更擅长预测不是武器的物体,并且在正确的区域周围有边界框。
Mobilenet(图片由作者提供)[点击此处查看大图]
Mobilenet LIME(图片由作者提供)
VGG16
- 在下面的例子中,VGG16 无法像我们自己构建的架构那样区分非武器。它错误地将 3 张手枪图像中的 1 张归类,而将其余的正确归类为手枪
- 尽管它错误地将手枪归类为无武器(右边第四个),但边界框并不在枪上,因为它停留在握枪的手上。
VGG16(图片由作者提供)[点击此处查看大图]
VGG16:石灰(图片由作者提供)
结论
- 这个项目的目标是创建一种算法,它可以将自己集成到传统的监控系统中,并比人更快地防止糟糕的情况(考虑到当今社会的不幸情况)。
- 虽然这很酷,但我的电脑中的硬件还没有。分割图像并处理图像的每个部分大约需要 10-45 秒,这对于直播视频来说太慢了。
- 我上面展示的视频演示是一个 30 秒的剪辑,大约需要 20 分钟来处理。
- 然而,尽管使用 RX 580 进行视频直播是不可行的,但使用新的 Nvidia GPU (3000 系列)可能会有更好的效果。
- 此外,这种技术可用于追溯检查事件,如人体摄像机镜头或抗议。
注意如果您想了解整个项目,请访问我的 GitHub **
如何为您的数据科学项目构建 Web 应用程序
如果你在电脑上处理数字,它会发出声音吗?
我最近使用深度强化学习为哲学家足球建造了一个人工智能球员,以纪念已故的约翰·康威。我想要一种向人们展示结果的方式,所以我创建了一个网站哲学家。足球在那里你可以和机器人或其他人比赛。
你可以在那里和人工智能对战,感受一下它和游戏,尽管人工智能还在训练中——可能需要几个星期才能变好。
一路走来,我不得不处理一个弄清楚大量的技术,让我的模型从 Jupyter 笔记本代码到部署。现在,如果我每读一个“教程”就有一美元,我不能安装谁的软件,我不能运行谁的代码,或者那些没有告诉我如何部署任何东西,我会…嗯,我不会很富有,但是我可以负担更多的硬件来训练我的模型。我的人工智能播放器现在会更好。
我尤其感到沮丧的是,一行一行的代码示例都在做作者所做的事情。一旦你想做一些不同的事情(比如你自己的项目),他们就不那么有帮助了。(我的意思是,我仍然使用它们)。
所以,我的解决方案是给你写一本一体化的指南,作为相关技术的地图,它们做什么的感觉,以及要使用的资源。希望有了这个,你能想象出把你的项目变成一个网络应用的途径。以我的经验,这永远是第一步。
作为奖励,感谢 Docker,我能够保证,如果你克隆我的 github repo 并按照指令操作,代码就能保证运行。至关重要的是,这意味着你可以修改它并了解它的功能。
特别是,当我开始的时候,我真的对互联网的运作只有一个粗略的了解,对我的项目如何在互联网上结束没有一个清晰的概念。
所以,说清楚一点,这是不是
- 这不是关于工业级部署技术的教程。这是一个业余爱好项目。你将会学到很多关于那些工业级技术解决了什么问题。
- 这不是一个逐行走查的教程。
- 这绝对不包括安全。我的网站肯定有密码和一些内置的安全功能,但只是很好,好吗?
- 这绝对不是开发 web 应用程序的最佳实践。我不是网页设计师或开发人员。我做的东西甚至不能在你的手机上运行。但它肯定能在你的电脑上运行,就像你自己看到的一样。加上你大概(?)不会吐槽你的配色。
- 这不是关于如何创建模型或使用数据库(核心数据科学的东西)。这是关于如何部署您的模型,可能使用数据库。
“堆栈”
也就是我使用的所有技术的可怕列表。下面我会解释重要的,我保证。这是目录。
核心技术
这些是我真正花时间思考和编写代码的事情。
- (后端/API) Django ,一个 Python web 框架/包,使得处理数据库和构建 API 变得轻而易举。在这里添加相关但技术上独立的标准 API 的 Django REST 框架和支持 WebSocket 连接和异步计算的 Django 通道(模型与服务器并行运行)。
- 一个来自脸书的 JavaScript web 框架/库,可以在浏览器中制作漂亮的图片。
- (机器学习) PyTorch 一个用于神经网络的机器学习框架/Python 包 thingamajig。也来自脸书。
- (部署/虚拟化) Docker 一个虚拟机,它通过发誓自己不是虚拟机来定义自己。出于我们的目的,它的功能有点像类固醇(ish)上的 conda(或 pip)——它神奇地解决了安装软件的所有问题。
非常重要,因为你可能希望以后在服务器上运行你的代码,而重新安装所有的软件将是一件痛苦的事情。(你的服务器运行 Linux,没有鼠标)。
其他花哨的词语
我使用过但几乎不用接触的技术
- (数据库) PostgreSQL :好看,免费,开源,是一个数据库。Python 的内置 SQLite 不能做一些稍微不太基本的事情,太糟糕了。
- (键值存储) Redis 不完全是 SQL 数据库。在这个应用程序中,它充当一个队列,这样您的模型就可以与面向 web 的服务器应用程序并行运行。当您的模型完成时,它会让实际的服务器知道。
- (网络服务器) nginx 一个免费的开源网络服务器。Django 也是一个 web 服务器;回想起来,我可能会使用 in。
- (托管)数字海洋它就像谷歌云或亚马逊网络服务(AWS)或微软 Azure,但复杂性大大降低。非常容易设置。而且(至关重要的)非常便宜。
- (培训)用于 GPU/TPU 访问的 Google Colab 和用于支持它的持久存储的 Google Drive 。同样,比其他选择便宜得多(我已经花了 300 美元在免费的谷歌云(GCP)上)。注意:我确实花了很多时间在这个上面,但是它的功能就像一个没有任何好的键盘快捷键的 Jupyter 笔记本。所以没什么工作量。
二等奖;荣誉奖;H 奖
- 决定事物实际外观的语言。出于某种原因,设计用户界面(比如网站)的准则最小惊奇原则并不适用于制作用户界面的语言。
- 来吧,你知道这个。你以前在 Neopets 或 Myspace 上做过主页,对吗?
好了,是时候开始真正的指导了。这个计划是从结束时的样子到如何开始逆向工作
完成的应用程序
最后,你的代码将如何与你的用户联系起来,用户会认为你的网站“哇,太酷了”
你要付钱给某人(很便宜,大概 5 美元/月)让他帮你运行一台电脑。据你所知,这是一台真正的电脑。但实际上它是一台虚拟计算机,这是一个运行在具有 Pinnochio 复合体的真实计算机上的程序(它也认为它是一台真实的计算机)。美就美在无所谓;它很擅长伪装。
不像你的电脑,那台电脑将有一个真正的互联网连接,人们可以找到它。而且它会 24/7 全天候开机,足不出户,不出 wifi 范围。
那台计算机将运行 Docker,Docker 将运行你的代码。你也可以不使用 Docker,直接在电脑上运行,但这很麻烦。
客户端-服务器架构,又名互联网
好了,你的代码要做什么?互联网上唯一发生的事情就是电脑获取信息,接收信息。就像使用美国邮政署(USPS)一样,但它没有破产,它以光速加上交通速度移动(总是有交通)。在互联网上,这些消息被称为“数据包”
数据包大致有不同的类型,称为协议,就像 USPS 的东西(第一类,垃圾邮件,认证邮件,包裹,媒体邮件等。).现在我们只担心常规的旧 HTTP。你从像http://哲学家.足球这样的网址开始就知道的那个。
当有人访问你的网站时,他们会发送一个 HTTP GET 请求,如下图中的#1 所示。简而言之,他们会发送一封邮件,上面写着“请把你的网页发给我。”返回的是一堆要显示的东西(超文本标记语言——HTML),如何显示的指令(层叠样式表——CSS),最重要的是一堆要运行的代码(JavaScript)。浏览器基本上只运行 JavaScript,所以你必须写一些 JavaScript 来运行。
你的网站拿起“收音机”说“罗杰,200,这是网站。”数字(200)表示“好的”你可能在各种场合都遇到过老好人404(400 和 500 的意思是:事情不妙,请别烦我)。代码 418 “我是一把茶壶”意味着服务器不会煮咖啡,因为它永远是一把茶壶。(说真的。如果是我编的,我现在会站着说话)。
注意到站在你的网站和左边的笔记本电脑之间的那个盒子了吗?这是真正的网络服务器。在工业级应用中,它可以做各种新奇的事情。在我们的例子中,它只是接收消息并决定将它们发送到哪里。就像办公楼里的收发室。我使用了 nginx,但是 Django(见下文)也完全能够做到。我只是不想把所有的前端代码放在后端代码里面。看起来很尴尬。
好了,现在你在别人的笔记本上运行代码了。你被录取了。不要试图对他们的电脑做任何坏事,这是可笑的过度起诉的联邦罪行。(而且浏览器有相当好的安全性;它不会让你太做太多坏事)。你的 JavaScript 代码能做什么?它可以在笔记本电脑上渲染动画,当用户点击页面上的某个地方时,它可以决定做任何它想做的事情,和它可以自己与你的服务器对话。
姜戈
很好,现在你知道你的代码将如何运行了。那密码到底是什么?第一部分是服务器。
Django 是 Python 中的一个 web 框架。Django 教程相当不错,所以我不会深入讲解。它是做什么的?
- 它处理建立数据库(创建表)以及写入和查询它。它构建的抽象非常好,所以您不需要直接编写 SQL 查询(如果您愿意,也可以编写更复杂的查询)。
- 它处理“服务器端路由”这意味着,例如,如果有人导航到 www.yoursite.com/page1,,他们会得到你决定的第一页应该是什么。相反,如果他们去 www.yoursite.com/page2,,他们会得到别的东西。
- 它允许您运行 python 代码来决定页面上的内容。例如,如果有人访问某个页面,您可以在数据库中查询他们的用户名,并显示他们的帐户信息(如他们的生日,如果他们之前告诉过您)。
- 它处理所有的 HTTP 诡计,比如读取传入的消息并发送适当类型的响应。
- 它还能做很多其他的事情,比如安全、认证等等。
除了普通的 Django 之外,您可能希望建立一个 api 来返回数据而不是网页。可以用 djangorestframework 来做这个(教程也挺好的)。这提供了两个关键的扩展:
- **串行化器。**这些将数据从数据库中的数据结构转换成可以发送给浏览器和客户端应用程序的数据结构(通常是 Javascript-object notation — JSON)。
- API 视图。一个简单的装饰器,用于处理不同类型的 HTTP 请求,比如 GET (“send me some data “)、POST(“这里有一些与数据库中已有内容相关的数据”),或者 PUT(” upload/create a whole record to database。”)
最后,你可能希望一些事情快点发生。HTTP 协议被设计成每当客户端发送一条消息时,它应该几乎立即得到响应。如果它不发送信息,它就不能接收信息。如果您需要等待,这是一个问题,因为您的模型需要一点时间来处理传入的数据。Websockets 是这方面的一个解决方案,并得到 Django 频道的支持(也有很好的教程)。有了 Websockets,你可以随时发送消息,甚至可以发送多条消息。
反应
React ,脸书的一个产品,是另一个用于编写在客户端运行的代码的“网络框架”。同样,它有一个很好的教程,所以我不会进入太多的细节。
基本结构是你看到的一切都是一个“组件”组件可以是无状态的,也可以不是。如果是无状态的,它们就像一个函数:它们接受参数并产生一个输出,即一些将由客户端浏览器显示的 HTML。它们也可以是有状态的。有状态对象可以记住事情。例如,如果你正在构建一个棋盘游戏,状态可以是棋盘的位置以及该轮到谁了。
另一件要记住的事情是 react-router 提供的“客户端路由”。这样做是为了让你只需要给你的用户发送一页。然后当他们去 www.yoursite.com/page1,时,你的 React 应用程序查看 URL 并决定呈现第 1 页。同样,对于 www.yoursite.com/page2,,他们得到第二页渲染。但是他们不需要每次都与服务器对话:他们只需要得到一个知道查看 URL 并决定显示什么的页面。
码头工人
Docker 是我发现最讨厌缠头的技术。我认为这是因为它坚持使用自己的词汇,并且通过不是虚拟机来定义自己,即使它解决了完全相同的问题。
Docker 的要点是,有了这项技术,你可以用代码定义一个你的代码将在其中运行的环境。有点像类固醇的虚拟环境。用简单的英语来说,这些说明可能是这样的:
- 创建新的“虚拟”计算机
- 安装 Python 3.6
- 从我的 Github 库复制我的 Django 代码
- 运行命令启动 Django 服务器
这些指令放在 Dockerfile 文件中。Docker 文件被构建到 Docker 映像中。最后,你可以基于这个图像创建一个容器。它是运行代码的容器。
Docker 的关键点是这段代码在每台计算机上都是一样的。让你的主机提供商(比如 Digital Ocean)给你一个已经安装了 Docker 的服务器是非常容易的。然后你所要做的就是根据你想要的图像启动一个 docker 容器。
此外,您可能想要一个以上的图像。例如,一个映像可以运行数据库,一个映像可以运行后端服务器,一个映像可以运行前端服务器。
Nginx
我最终使用 Nginx 作为主要的网络服务器。因此,当你访问客户端时,nginx 首先会查看你试图访问的 URL,然后将它路由到 Django,或者提供一组 React 文件来决定客户端路由呈现的内容。
您也可以用 Django 来完成这个任务,这可能会更容易。
结论
如果你心中有一个项目,并通过 Django 和 React 教程来完成,你应该会遇到你需要做的其他事情,并能够泰然自若地处理它们。希望这个路线图能给你一个好主意,告诉你从哪里开始!
如何使用 Plotly 和 Dash 用 50 行代码构建 Web 应用程序
构建企业级应用从未如此简单
来源:https://unsplash.com/photos/mcSDtbWXUZU
有很多工具可以实现数据可视化。然而,很少有工具能够让您轻松构建基于 web 的交互式高质量绘图应用程序,从而将您的数据可视化游戏提升到一个新的水平。
这就是 Plotly 和 Dash 所提供的,全部用 Python 编写。这就是为什么你要 100%了解它!
在本文中,我将展示如何使用 Plotly 创建基本的交互式绘图,以及如何使用 Dash 将它们集成到基于 web 的应用程序中。
使用的数据将是商店的每日销售额,以及 7 天的预测。每日销售数据如下所示:
1.PLOTLY
Plotly 是一个图形库,可以制作高质量的交互式图形。关键词是互动。下面是绘制两行的代码,一行用于每日销售额,另一行用于预测。
**#Import plotly** import plotly.graph_objects as go**#Initialize the plot** fig_totalsales=go.Figure()**#Add the lines** fig_totalsales.add_trace(go.Scatter(x=sales[sales.columns[0]], y=sales[sales.columns[1]], visible=True, name='Sales'))fig_totalsales.add_trace(go.Scatter(x=fore['Date'],y=fore[fore.columns[0]],visible=True, name='Forecast'))**#Add title/set graph size** fig_totalsales.update_layout(title = 'Daily Sales', width=850, height=400)fig_totalsales.show()
很漂亮吧? append.traces 函数允许我们轻松地向图形中添加其他线条。
Plotly 提供了更多。在本文中,我将把它留在这里,因为我想更多地关注 Dash。尽管如此,我还是强烈推荐你查看 Plotly 文档:https://plotly.com/python/。
2.破折号
Dash 平台允许我们这些新手开发高质量的企业级分析应用程序,而不需要开发人员、JavaScript 或任何超出基本 Python 技能的东西。
有了 Dash 开源(免费版),Dash 应用可以在本地工作站上运行。你不能分享应用程序本身,但你可以分享代码。
每个完整的 Dash 应用程序都有三个部分:
- 布局,它描述了应用程序的外观。
- Dash 核心组件(称为 dcc),基本就是应用的内容(图形、下拉菜单、复选框等)。).
- 回调,通过组件之间的交互,使应用程序具有交互性。
下面是创建包含我们之前创建的表的 Dash 应用程序所需的所有代码:
**#Import libraries** import dash
import dash_core_components as dcc
import dash_html_components as html**#Create the app** app = dash.Dash()
app.layout = html.Div([
dcc.Graph(figure=fig_totalsales)
])
app.run_server(debug=True, use_reloader=False)
仅此而已!越简单越好。我们看到这个应用程序包含一个布局和一个核心组件,即图。
html.div 标签定义了应用程序中的一个新部分,我们可以在其中添加任何我们想要的 Dash 核心组件(dcc。图在这里)。下面是这个“应用程序”现在的样子:
当然,如果有人想创建一个应用程序,这并不是展示一个单独的情节。任何 python 库都可以做到这一点。Dash 的伟大之处在于,你可以让不同的组件相互交互。这就是 Dash 的第三部分,回调,发挥作用的地方。
回调请求一个输入,让它通过一个函数并给出一个输出。让我们看看如何改进我们当前的应用程序(我们可以做一百万件事情,这里有一些)。
最终应用创意
1。添加标题。
2。添加过滤我们的数字的功能,以便只显示特定日期范围的数据点。**
3。添加一个交互式文本框,显示所选期间的销售总额。
Dash 有一个核心组件叫做 DatePickerRange 。我们所需要的只是一个回调,将图形和文本与这个新的核心组件链接起来。
下面是实现这一点的完整代码。
下面是对代码的逐块描述:
- 第一个 html.div 用于创建标题为应用程序的部分。
- 第二个 html.div 添加了 DateRangePicker 组件以及将通过回调链接到它的两个输出(一个文本元素和一个图形)。
- 最后一部分是回调。它要求两个输入,即两个日期,并给出两个输出,即图形和文本。在 update_output 函数中,根据提供的日期选择数据。然后,计算销售总额并创建数字。
下面是一个简短的视频,展示了这个应用程序现在的样子:
现在你有了它,一个不到 50 行代码的体面的基于 web 的应用程序!查看更多关于 Dash 的信息:https://dash.plotly.com/。
非常感谢你的阅读!
如何用 Python 构建 Web Scraper
快速抓取、汇总谷歌搜索引擎结果
网页抓取
网络抓取是分析师筛选和收集大量公共数据的绝佳工具。通过使用与讨论主题相关的关键词,一个好的网页抓取器可以非常快速地收集大量数据,并将其聚集成一个数据集。Python 中有几个库使得这一点非常容易实现。在这篇文章中,我将展示一个架构,我一直用它来抓取和总结搜索引擎数据。这篇文章将被分成以下几个部分…
- 链接抓取
- 内容抓取
- 内容总结
- 建设管道
所有的代码都将在这里提供。
链接抓取
首先,我们需要一种方法来收集与我们收集数据的主题相关的 URL。幸运的是,Python 库 googlesearch 使得收集响应初始 google 搜索的 URL 变得很容易。让我们构建一个类,使用这个库来搜索我们的关键字,并将固定数量的 URL 附加到一个列表中,以便进一步分析…
内容抓取
这可以说是 web scraper 最重要的部分,因为它决定了网页上的哪些数据将被收集。使用 urllib 和 beautiful soup (bs4)的组合,我们能够在链接抓取器类中检索和解析每个 URL 的 HTML。Beautiful soup 让我们指定想要从中提取数据的标签。在下面的例子中,我建立了一个 URL 请求,用 bs4 解析 HTML 响应,并存储在段落(
)标签中找到的所有信息…
内容总结
在这里,我们创建了一个文本摘要,它是从驻留在我们的内容抓取器中的每个页面的 HTML 中提取的。为此,我们将使用库的组合,主要是 NLTK。我们生成摘要的方式相对来说很简单,有很多方法可以改进这种方法,但这是一个很好的开始。在对填充词进行一些格式化和无效化之后,单词被标记化并按频率排序,生成一些旨在准确概括文章的句子…
建设管道
这是我们把所有东西放在一起的部分。一个类将根据需要实例化每个其他组件的实例,以构建和实现我们的 web scraper。WebScraper 类有几个参数…
- 搜索 —字符串,搜索引擎查询
- n —整数,要分析的 URL 源的数量
- sl—整数,总结的句子长度
- fall_through — Boolean,是否多线程化进程
- Write _ file—布尔型,将摘要写入文件
现在让我们实例化并运行这个 WebScraper 类的一个实例…
Analyst(‘AAPL’, 10, 3, False, False)
运行前面的代码会产生以下输出…
Analyzing: http://t1.gstatic.com/images?q=tbn:ANd9GcSjoU2lZ2eJX3aCMfiFDt39uRNcDu9W7pTKcyZymE2iKa7IOVaIAnalyzing: https://en.wikipedia.org/wiki/Apple_Inc.
Analyzing: https://www.bloomberg.com/news/articles/2020-08-26/apple-plans-augmented-reality-content-to-boost-tv-video-serviceAnalyzing: https://www.marketwatch.com/story/apple-stock-rises-after-wedbush-hikes-target-to-new-street-high-of-600-2020-08-26Analyzing: https://www.marketwatch.com/story/tesla-and-apple-have-had-a-great-run-heres-why-theyre-poised-to-rocket-even-higher-in-the-next-year-2020-08-26Analyzing: https://finance.yahoo.com/quote/AAPL/Analyzing: https://seekingalpha.com/article/4370830-apple-sees-extreme-bullishness
Analyzing: https://seekingalpha.com/news/3608898-apples-newest-street-high-price-target-700-bull-caseAnalyzing: https://www.marketwatch.com/investing/stock/aaplAnalyzing: https://stocktwits.com/symbol/AAPLencoding error : input conversion failed due to input error, bytes 0x9D 0x09 0x96 0xA3
encoding error : input conversion failed due to input error, bytes 0x9D 0x09 0x96 0xA3Value Error***For more information you can review our Terms of Service and Cookie Policy.For inquiries related to this message please contact our support team and provide the reference ID below.***URL ErrorValue Error"China remains a key ingredient in Apple's recipe for success as we estimate roughly 20% of iPhone upgrades will be coming from this region over the coming year."
Ives points to recent signs of momentum in China, which he expects will continue for the next six to nine months.
Real-time last sale data for U.S. stock quotes reflect trades reported through Nasdaq only.***By comparison, Amazon AMZN, +0.58% has split its stock three times, rallying an average of 209% the following year.
Apple�s history isn�t quite as stellar as all those, with its four previous splits resulting in an average gain of 10.4% in the following year.
Real-time last sale data for U.S. stock quotes reflect trades reported through Nasdaq only.***The truck and fuel cell maker �could be a major horse in the EV race,� wrote the analyst, while voicing concerns about the stock�s valuation.
stocks edged lower Wednesday, a day after the S&P 500 set its first record close since February, after Federal Reserve officials highlighted the uncertainties facing the economy.
stock-market benchmarks mostly opened higher on Wednesday, pushing the key benchmarks to further records after an economic report came in better than expected.***Apple is the world's largest information technology company by revenue, the world's largest technology company by total assets, and the world's second-largest mobile phone manufacturer after Samsung.
Two million iPhones were sold in the first twenty-four hours of pre-ordering and over five million handsets were sold in the first three days of its launch.
The same year, Apple introduced System 7, a major upgrade to the operating system which added color to the interface and introduced new networking capabilities.***Howley also weighs in on Nintendo potentially releasing an upgraded Switch in 2021.***[Finished in 5.443s]
我们已经成功地从关于 AAPL 的热门搜索结果中提取了一些摘要。如控制台输出所示,一些站点阻止了这种类型的请求。然而,这是一个全面的 Python web 抓取入门指南。
如何用 Python 构建 wordcloud
一个简单快速的建立文字云的教程
图片由安吉莉卡·洛·杜卡(作者)提供
在本教程中,我将向你展示如何使用wordcloud
包在 Python 中构建文本的单词云。
在示例中,我将构建圣奥古斯丁的忏悔的词云,可以从Gutenberg 项目页面下载。这部杰作被分成 13 本书。我们将每本书存储在不同的文件中,名为 number.text(例如 1.txt 和 2.txt)。每个文件的每一行只包含一个句子。
本教程的源代码可以从我的 Github 资源库下载。
入门指南
安装并熟悉 wordcloud 软件包
创建 word cloud 的第一步是通过命令pip install wordcloud
安装 wordcloud 包。然后你可以导入WordCloud
这个类以及STOPWORDS
的列表。
from wordcloud import WordCloud, STOPWORDS
如果需要,您可以将其他停用字词添加到列表中。
stopwords = set(STOPWORDS)
stopwords.add('thou')
Wordcloud
函数需要一个句子作为输入,其中包含应该计算单词云的所有单词。在我们的例子中,我们应该将杰作的所有文本存储到一个变量中。我们可以通过打开相关文件来读取每本书的文本,并将其存储到一个全局变量中,这个变量叫做all_text
。
all_text = ""
for book in range(1,14):
file = open('sources/' + str(book) + '.txt')
lines = file.readlines()
for line in lines:
all_text += " " + line
构建单词云
为你的文本创建世界云
现在我们已经准备好构建 wordcloud 了。我们可以创建一个WordCloud
对象,向它传递单词云的大小、停用词列表、背景颜色和最小字体大小。
wordcloud = WordCloud(width = 800, height = 300, stopwords = stopwords,background_color ='white', min_font_size = 10)
一旦构建了WordCloud
对象,我们就可以调用方法generate()
来计算作为参数传递的文本的 worcloud。
wordcloud.generate(all_text)
绘图结果
将单词云保存到图片中
最后,我们准备绘制结果。我们可以利用matplotlib
库提供的imshow()
函数。
import matplotlib.pyplot as plt
plt.figure(figsize = (8, 3), facecolor = None)
plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout(pad = 0)
plt.savefig('plots/word_cloud.png')
plt.show()
吸取的教训
用 Python 构建一个文本的单词云非常简单。这可以通过wordcloud
包完成
- 第一步是定义一个字符串变量,它包含所有需要计算单词云的文本。
- 然后,
Wordcloud
对象可以通过指定一些参数作为实参以及向其方法generate()
传递文本来定义 - 最后,利用
matplotlib
包可以完成一个 wordcloud 的情节。
如何为时尚图像构建人工智能
现实世界中的数据科学
为什么每个时尚网站都应该接受机器学习,如何使用分类法和数据,以及如何使用 Flows 构建图像识别系统
在这篇文章中,我将向你展示如何建立你的时尚识别系统。我将重点介绍鞋类,不过这种方法也适用于其他服装。这种方法不仅适用于时尚领域。好消息是这个教程是免费的!
为什么机器学习和时尚?
有几个原因:更好的用户体验,不断增加的点击率,参与度,收入…这里有一个大的研究,在时尚零售中,每个商店都应该具备的最重要的特征是什么:
正如你所看到的,一些功能纯粹基于机器学习和视觉 AI。因为新冠肺炎的隔离,世界上大多数的时装店都关门了,所以电子商务现在是零售业最重要的部分。如果你有一个时装店网站,那么考虑采用一些机器学习功能。它也可以帮助您的业务!这是改进您的数字产品的最佳时机。
插图由维塔·瓦尔卡创作
视觉人工智能在时尚领域最常见的使用案例:
- 搜索 : 向你的客户展示最相似的产品
- 标记 : 通过更好的分类,让您的产品集合更易于搜索
- 抓拍&获取 : 允许您的顾客拍摄服装照片,并在您的店铺中找到它
- 虚拟试衣间 : 我穿这件衣服怎么看?
- 测脚 : 这双鞋适合我吗?
- 和许多其他人
最好的功能之一是向您的客户展示视觉上相关的备选方案。这些备选方案可以由预测的标签生成/过滤。
分类学
思考分类是最重要的步骤之一!这是一个非常困难的问题!
在开始之前,我们应该定义鞋类识别系统的分类->换句话说,我们想要识别哪种标签。当我们在做鞋的时候,有数千种款式,数百种不同的图案。例如,有适合休闲时间的运动鞋,也有适合跑步的运动鞋。整个鞋类分类可能很复杂,因为我们可以识别的款式、颜色、品牌、图案、鞋跟尺寸、…。我们之前说过,仅仅是风格本身就可以有很多选择。总是从简单的事情开始!
每当你在有成千上万种选择的领域工作时(时尚、房地产等等),要非常小心。 时尚分类的问题在于没有统一的标准 。每家服装店都使用不同的名称和分类。看看 urbanoutfitters.com 的 ASOS 网站或者就知道了。我的建议是不要发明轮子。只要看看一些最著名的服装店,看看他们是怎么做的,就会受到启发。
优雅的蓝色高跟鞋。安德鲁·唐劳在 Unsplash 上的照片
风格:可以说我们可以识别优雅、休闲、运动。如果我们认可优雅,那么我们可以根据性别来划分这个类别。大多数时候,女人优雅的鞋子看起来和男人的有点不同。在休闲类中很难说,在运动/教练类中就更难说了。大多数女性优雅的鞋子也可以通过鞋跟的高度来识别。
子类:如果我们认出了优雅的鞋类,那么我们可以说是否有休闲鞋、靴子或高跟鞋。如果我们谈论运动,那么我们可以把它们分为运动鞋,跑步,徒步旅行。
颜色:红色、绿色、蓝色、黄色、黑色、白色、橙色……我建议只预测基本颜色。你引入的颜色越多,你的标签和图片就越不一致。用颜色给产品贴标签可能会很棘手!每张图片在不同的屏幕上都会有一点不同。新的 MacBooks 拥有 true tone 技术,可以根据环境照明将显示器的颜色调整为暖色。如果你在一个团队中工作,人们看到的颜色会有点不同。
品牌:品牌在一些运动鞋中很容易辨认。阿迪达斯、耐克、彪马运动鞋大部分时间都含有大的标志或其他视觉特征。穿优雅的鞋子会困难得多。也许来自汤米·席尔菲格的优雅的鞋类可能更容易,因为品牌标志几乎总是可以在鞋上看到。
把所有这些放在一起,我们简单的鞋类分类可以写成层次结构,并通过层次分类来解决。分级分类意味着我们将调用多个模型,如果我们预测一些标签,我们也可以根据预测的标签调用另一个模型。例如,如果我们预测优雅的标签,那么我们将调用预测鞋跟尺寸的模型。如果我们预测运动品牌,那么我们可以调用其他模型。
我们对鞋类的简单分类。您可以根据需要以任何方式扩展或调整它。在 Miro 应用程序中绘制。
数据和机器学习
机器学习系统对他们看到的数据有偏见。
简单地说,机器学习是一种优化技术,它试图从你的数据中学习重要的知识。
在使用人工智能/机器学习时,思考这一点也是另一个关键步骤。你的系统将仅用于产品照片:背景大部分时间是清晰的,还是照片由专业相机拍摄?那么您的数据集应该包含类似的图像。
如果你正在使用一个移动应用程序来拍摄真实背景下的鞋子图片,那么就在你的数据集中包含这种图片。获得" 真实世界 "图片可能会困难得多,但是,如果您的产品能够在"质量较差"的图片上表现良好,它可能会更有价值。您的应用程序应该在所有可能的环境中工作吗?那么你应该有高质量和平衡的数据集。
我们需要下载我们的数据。我们可以使用谷歌或必应搜索并从图片页面手动下载一些数据。如果你知道如何编程,那么你可以写一个从 google 或 bing 下载图片的脚本。例如,您可以使用 scrapy 库从商店页面抓取图像。对于非编码人员来说,使用 Chrome 或 Firefox 插件从网站上获取图片也是一个很好的选择。
我们应该为每个标签下载多少张图片?
关注迭代方法,如果你正在使用迁移学习技术,每个标签 50 张图像对于开始训练你的模型是相当不错的。如果效果不好,那么继续增加图像的数量。你的分类法越复杂,你需要的图像就越多。有时每个标签需要数百张图像。
创建你的模型
因为这是针对没有机器学习和编程经验的人的教程,所以我们准备用 Ximilar 平台创建一个识别系统。别担心,做起来很容易。
登录 Ximilar App 然后点击图像识别服务。转到“任务”页面,创建一个新任务。然后选择分类任务,并为该任务创建和连接标签。Task 代表了一个机器学习/深度学习模型。
创建标签为优雅、休闲和运动的识别任务/模型。
下一步是训练你的模型。只需点击火车按钮。
您的鞋型模型包含三个需要识别的标签/类别。
这样,我们可以为颜色、材料、图案等创建其他任务
通过流程将您的任务/模型连接到层级
现在,我们将使用一个创新的功能,允许您通过几次点击将机器学习模型连接到分类/层次结构中。从仪表板转到流服务并创建新的流。然后创建一个“列表操作”作为第一步。然后在带跟任务的样式识别任务上添加“分支选择器”。最后,将颜色、类别、性别和品牌任务添加到列表中。
建立具有流程的鞋类分级分类系统
展开
您的整个系统都部署在一个易于使用的 API 端点和流之后!您可以将移动应用程序或网站连接到 API。最终的 app 可以是这样的:
这是一个示例,展示了鞋类产品的图像识别是如何工作的。
总结
机器学习在零售业无处不在,有助于增加收入和改善用户体验。
- 在构建机器学习系统时,请始终考虑您的分类法和标签。
- 与您的数据和标签保持一致。进行增量开发,从简单的开始,然后继续开发更复杂的解决方案。
- 你可以使用流来构建一个层次分类系统。
查看我们的视觉时尚人工智能系统的 演示 。如果你想创造一个时尚服装探测器,请看下一篇博文(关于微控制器,但同样适用于时尚):
根据 Kaggle 竞赛数据检测微控制器的简单教程
towardsdatascience.com](/detecting-microcontrollers-with-cnn-ced688a8a144)
如何为 Twitter、Pinterest 和亚马逊构建 AI
从做过所有(或至少是大部分)事情的人那里得到的教训
高耀太科学公司首席执行官道格拉斯·梅森——图片来源:作者
我们采访了 道格拉斯·梅森 ,高耀太科学的数据科学家兼首席执行官,他正在建立机器学习模型来预测新冠肺炎疫情。他自由地分享了他从十多年的数据科学工作中学到的智慧和经验,从他在哈佛的博士学位到他在 Pinterest、Twitter 和 AWS(亚马逊)等大公司的时间。
道格拉斯的背景
道格拉斯走上了成为数据科学家的独特道路。虽然在成长过程中,计算机一直是他家庭的一部分,但他认为它们很无聊,他告诉家人他将“永远不会学习计算机科学”
相反,道格拉斯去了南加州大学学习电影制作,认为他会跟随他的梦想成为一名电影导演。很快,他意识到电影制作学校并不像他想象的那样,于是他选择了古典吉他。从那里,他意外地发现了对理论物理的热情,他觉得这很迷人(而且报酬比弹吉他高)。
不久之后,道格拉斯发现了自己对数据科学的兴趣,并一路攀升,直到他成为 Twitter、Pinterest 和 AWS 的工程和数据科学团队的负责人。他形容在这个领域工作就像生活在科幻电影里一样。
“当我从事这类工作时,我感觉自己就像奇异博士——仿佛我在多元宇宙中。你真的可以过瑞克和莫蒂的平行宇宙生活。”
但是道格拉斯并不满足于利用他的专业知识来提高大公司的收入,所以他继续创立了自己的公司,高耀太科学公司。他目前专注于建立新冠肺炎模型来预测疾病爆发。
从航运机器学习项目中吸取的经验教训
道格拉斯的成功并非没有一些来之不易的教训。他向我们讲述了他在参与的许多团队和项目中遇到的一些挑战。
第一课:使用机器学习与用户合作,而不是接管
在推特上,道格拉斯制作了一个名为“关注谁”的专题。这为 Twitter 用户提供了个性化的推荐,告诉他们哪些账户可能会让他们感兴趣。作为一名数据科学家,道格拉斯发现人们大量使用这一功能。起初,它似乎很棒——人们几乎追随了它推荐的每一个人。但从长远来看,使用这一功能的人访问 Twitter 的次数更少了。
他们的订阅源充满了由算法选择的推文,而不是他们自己选择的人的推文,这些推文实在是太多了。
通过减少“跟随谁”的建议数量,道格拉斯提高了的长期参与度。
众所周知,长期目标和短期目标经常发生冲突,但道格拉斯在这里发现了更深刻的教训。随着机器学习解决方案变得越来越强大,使用它们做太多事情往往很有诱惑力。这几乎总是一个错误。道格拉斯说:
“我的目标是打造与用户一起工作的产品,而不是试图取代用户。”
科幻小说中描绘的人工智能——具有人类水平的智能——可能是许多人试图用机器学习做太多事情的原因。在许多情况下,它最好用于增强人类的行动,而不是取代它们。
第二课:数据管道和好的工程比数学和算法更重要
人们对新的机器学习算法感到非常兴奋。首先,我们有神经网络(NNs),然后是卷积神经网络(CNN),然后是生成对抗网络(GANs),变压器,等等。谈论和探索算法是有趣和令人兴奋的。
但是道格拉斯,一个自称的数学呆子,已经知道数学和算法往往会得到太多的关注,而真正的成功来自于**好的数据,好的工程,专注于客户的问题,和不被数学所困。**他说:
“算法能产生不同的结果是非常非常罕见的。几乎总是数据管道。在我的工作中,我已经能够通过数据管道减少 90%的错误,相比之下,通过更好的算法可以减少 75%。然而,每个人都想和我谈论算法,但没有人想谈论数据管道。”
我们使用隐喻将机器学习算法与神经科学家联系在一起,将数据管道与管道联系在一起,因此哪个引起普遍关注并不奇怪。道格拉斯通过专注于机器学习不那么迷人的方面获得了成功。在大多数情况下,决定使用什么数据以及如何将其呈现给算法比算法本身更重要。
关注客户的目标
如今,许多“人工智能初创公司”谈论的更多的是他们提供的解决方案,而不是他们解决的问题,道格拉斯已经学会了保持对客户目标的高度关注。有时这意味着将自己从机器学习更具吸引力的理论方面抽离出来。他说:
“作为一名数学家,我热爱数学的所有细微差别,很容易迷失其中。但事实是,有无穷多的数学有待学习。在专注于客户目标之前,把自己关在房间里学习所有的数学知识是不可行的。”
许多数据科学家不想听到的事实是,成功的机器学习解决方案通常不是关于创造新的、强大的和令人兴奋的东西。更多的时候,你需要的是从正确的角度看问题,并使用屡试不爽的方法。
与经验丰富的工程师一起工作并向他们学习
道格拉斯亲自设计了许多成功的机器学习解决方案,并领导了软件工程师团队,但他对自己的工程能力保持谦虚,并强调了固体工程 的重要性。
“在亚马逊,我让工程师做尽可能多的事情,因为他们在工程方面比我强。我很想给你另一个答案,但他们很有效率,他们很有想法,他们以前见过这些结构,所以他们知道实现细节。”
然而,这并非一帆风顺。道格拉斯承认让不同的专家一起工作的困难,特别是当高技术人员往往对微小的决定有非常强烈的意见时。
他发现的让所有人达成一致的最好方法是不断发布最小可行产品(MVP ),这将带我们进入下一课。
教训 3:总是构建最小可行产品(MVP)
Douglas 对 MVP 深信不疑,MVP 展示了解决方案的核心部分,即使缺少许多功能。在开发机器学习解决方案时,他的目标是每周交付一个新的 MVP 。
他用这些来:
- **避免陷阱:**如果一个项目耗时太长,构建 MVP 的难度甚至可以用来证明该项目应该在多年的努力付诸东流之前尽早结束。道格拉斯说:
“如果事情变得更加困难,而我继续做 MVP,却从未达到目标,那么这就给了我们关于我们试图做的事情的难度的信息。”
- 沟通:无论是技术人员还是非技术人员,都倾向于更好地理解他们能看到和使用的东西,而不是抽象的想法。
“人们对事物抽象概念的反应,往往与他们看到真实事物时的反应完全不同。这就是为什么我总是推出 MVP。从更高层次的角度看问题的人可以获得必要的直觉,给我反馈。”
浪费两周的工作总比浪费两个月要好,MVP 可以帮助你做到这一点。
MVP 还有其他好处。通过发布解决方案的精简版本,道格拉斯经常发现少即是多。
“你最终交付的东西通常比你最初打算做的要简单得多,但它是精炼的。”
当然,当事实证明最佳解决方案是最简单的解决方案时,客户有时会不高兴。道格拉斯将构建机器学习解决方案比作创造艺术:这是关于投入开发的时间,而不是最终产品所需的努力。
“有一个经典的禅宗故事,讲的是一个国王雇佣了一位艺术家。这位艺术家工作了一年,但只用了三秒钟就完成了最后一幅画。当国王抱怨时,艺术家说,“哦,我花了一年时间试图画更难的东西。””
MVP 让你能够找到更好、更简单的解决方案,即使是在开发过程的后期,保持敏捷是很重要的,这样你就可以在必要时转向这些更好的解决方案。
人们经常认为某件事必须变得复杂才能变得强大,但事实上恰恰相反。
第四课:控制和精度比大小和力量更重要
大型机器学习模型,如 GPT-3,令人兴奋,经常成为头条新闻。但是道格拉斯将大型模型与早期(失败的)建造飞机的尝试相比较。这些飞机与莱特兄弟制造的著名的、成功的飞机竞争。是什么让他们与众不同?莱特兄弟专注于控制、、而他们的竞争对手则追求规模和力量。
莱特兄弟的独创性在于他们没有追求更大的引擎。他们是自行车修理工。他们甚至没有使用大功率发动机。相反,他们关注的是控制。”
这类似于机器学习模型。正如道格拉斯所说:
“我们做了一个最大的模型,可以做所有这些事情。但人们会问,‘我该如何解读这些东西?’“我怎么控制它,”我如何确保我的模型不会出轨?”
大型机器学习模型可能通常更强大,但除非它们解决了真正的问题,否则它们没有用。如果一个模型不可预测地产生惊人的结果并且只是在某些时候产生,那就没用了。如果一个模型产生准确的结果,但我们不明白为什么,也不能确定结果是否总是准确的,那么这也是没有用的。****
相反,更小、更简单、功能更弱的模型提供了更多的可解释性和 T2 一致性,在几乎所有情况下都更有价值。就像飞行一样,我们需要能够驾驶和着陆,而不仅仅是快速飞行。
成功运送机器学习项目
在我们与道格拉斯的交谈中,他分享了许多经验教训,但这些是他成功推出机器学习解决方案的最重要规则:
- 使用机器学习与用户合作:不要超越并试图接管他们。
- 多关注问题、数据、和工程而不是数学和新颖的算法。
- 专注于控制你产生的解决方案,而不是让它尽可能的宏大。
- 制造 MVP和定期运送较小的零件。即使他们没有所有的功能,紧密的反馈循环在这里比在软件工程中更重要。****
我们(数据营收)已经出货了几十个成功的项目。如果你需要帮助,不要犹豫,与联系。
如何使用 SQLite 构建会计系统
逆向工程 Xero 教 SQL
搔自己的痒处。
这是每当有人问我如何学习编程时,我总是给出的建议。实际上,这意味着你必须解决与你相关的事情或选择与你相关的项目——无论是在你的工作还是个人生活中。
盲目地去 Youtube 上的教程,阅读编程书籍,从 Reddit 帖子上复制代码等等。如果你开始学习编程,将一事无成。
学习 SQL 的最好方法
在这篇文章中,我将向您展示如何使用 SQLite 构建一个简单的会计数据库。
那么,为什么要创建一个会计数据库呢?为什么不直接复制公共数据,把它们推到 SQLite 中,然后从那里开始练习呢?
原因是创建一个会计数据库已经足够先进,可以涵盖数据库和 SQL 的所有方面——从查询到连接到视图和 cte。
作为一名会计出身的人,我认为这是学习 SQL 的最佳项目。毕竟编程是解决问题的工具。因此,不妨“解决”一个难点来全面学习 SQL。
通过观察 Xero 的工作方式,我获得了使用 SQL 创建会计系统的灵感。对于不熟悉的人来说,Xero 是一款起源于新西兰的云记账软件。它现在已经扩展到澳大利亚、美国、加拿大和英国。
Xero 的好处是它有一个漂亮干净的界面和许多可供选择的应用程序来扩展它的功能。
免责声明:我不是 Xero 的工程师或开发人员,这些观察可能不完全符合系统的工作方式,因为它总是在更新。当然,这里给出的 SQL 并不是 Xero 使用的 SQL 设计,因为他们的系统需要扩展。但这是一个非常有趣的项目,所以让我们做吧!
会计 101
在你兴奋过度之前,我们先来上一堂会计速成课。
基本的会计等式是
资产=负债+权益
这个等式基本上有三个部分
- 资产是实体的所有资源。
- 负债是公司所欠的。
- 而股权,是所有所有者的投资、图纸、盈利或亏损的积累。
右边描述了资产的融资方式——通过负债或权益。
我们可以扩展上面的等式来分解股权
资产=负债+期初权益+收入—费用
这 5 个账户——资产、负债、权益、收入和费用——是会计系统中常见的账户类型。
然后是借贷的概念。我可以继续深入讨论这两个问题,但在这篇文章中,你需要知道的是,在每笔交易中:
借方=贷方
这两个等式通常支配着整个会计周期中发生的事情。这两个等式也将作为创建我们自己的会计数据库的指南。
作为一名会计出身的人,我认为这是学习 SQL 的最佳项目。毕竟编程是解决问题的工具。因此,不妨“解决”一个难点来全面学习 SQL。
Xero 的会计实现
需要记住的重要一点是,Xero 的设计是为了让它在企业的日常运营中对企业主(而不是会计)有用。
因此,它围绕交易周期和内部控制进行设计。
交易周期
基本交易周期如下
- 销售周期
- 采购周期
- 资金周转周期
Xero 按如下方式实现这些循环
销售周期 销售使用发票输入 Xero。想象一下企业为销售(现金销售或赊账)开具实际的纸质发票。这正是 Xero 想要复制的东西。
发票可以直接从软件中打印出来,并按升序自动编号。
在引擎盖下,发票增加销售账户和应收账款(AR)账户。
采购周期 票据进入 Xero 使用票据。再一次,想象企业为购买(现金购买或赊账)出具实际的账单。这是公用事业和库存的通常情况。这也是 Xero 想要复制的东西。
这些账单可以直接从软件中打印出来,并可用于补充企业完成的任何审批程序。
在这个引擎盖下,账单增加了采购账户和应付账款(AP)账户。
现金周期 这涉及所有与现金有关的交易。有 4 种类型
- 发票付款 —未付发票的付款
- 账单支付 —未支付账单的支付
- 收到的款项 —不是发票付款的现金收据。这可能涉及现金销售,但如果你要开发票,请使用发票功能。
- 支出款项 —不属于票据支付的现金支出。这可能涉及现金购买,但如果你要开账单,使用账单功能。
这是关于交易周期的部分。
内部控制
对于内部控制,你需要了解系统账的概念。
Xero 有一篇理解系统账户的综合文章在这里。但出于我们的目的,我们将只讨论以下系统帐户
- 应收帐款
- 应付账款
- 银行账户(链接到银行馈送)
这些科目不能用在手动日记帐中。这意味着 Xero 希望您使用发票、账单和现金交易(发票支付、账单支付、收到的钱和花费的钱)来支持这些账户的余额。如果你愿意,这是 Xero 内部控制的实施。
当然,您可以通过在会计科目表(COA)中创建 AR、AP 和银行帐户来使用它们的非系统版本。但是,您不能对它们使用 AR、AP 和银行帐户类型。
需要记住的重要一点是,Xero 的设计是为了让它在企业的日常运营中对企业主(而不是会计)有用。
警告:我们不会掉进兔子洞
设计一个账户是非常复杂的。光是这个话题就需要多篇博文。因此,为了简单起见,我们将创建以下假设(不完全是 Xero 如何实现这些)
- 发票和账单支付 发票支付可以一次性支付两张或多张发票*。也就是说,我们不允许部分付款。账单支付也是如此。*
- 库存 我们不打算在这里使用库存项目。对于销售或采购,我们将直接使用库存帐户,而不是创建映射到库存帐户的库存项目。
我们的假设就是这样。设计完我们的数据库后,读者可以解除这些假设,尽可能多地模仿 Xero。
SQL 基础知识
现在,在重建我们版本的 Xero 数据库结构之前,让我们先来上一堂数据库速成课。
数据库是表的集合。每个表都由称为记录的数据行组成。这些列被称为字段*。*
处理数据库的程序被称为数据库管理系统或 DBMS。打个简单的比方,DBMS 之于 Excel 程序,数据库之于 Excel 工作簿,表格之于 Excel 工作表。
数据库和 Excel 工作簿有两个主要区别。
数据表示与数据存储是分开的。
也就是说,您不能通过直接查看数据并对其进行编辑来编辑数据库中的数据。(其他 DBMS 程序有 GUI,允许您直接访问数据库中的数据,并像编辑电子表格一样编辑它。但是在幕后,该操作发出一个 SQL 命令)。
表通常相互链接以形成关系。
关系可以是一对一、一对多或多对多。
一对一关系意味着“一个表中的一行只与另一个表中的一行相关,反之亦然”。一个例子是雇员姓名到纳税标识号。
这种类型通常包含在单个表 Employees 中,因为将数据分成两个表并没有什么好处。
一对多表示“一个表格中的一行只与另一个表格中的一行或多行相关,而不是相反”。一个例子是发票到发票行。一张发票可以有多个行,但一个发票行只属于一张特定的发票。
您可能已经猜到了,多对多意味着“一个表行只与另一个表中的一行或多行相关,反之亦然”。实现部分支付的系统就是一个例子。
一张发票可以通过不同的付款交易来部分支付,并且一次付款可以部分支付不同的发票。
数据库如何知道这些关系? 很简单。它是通过使用主键和外键实现的。
主键是区分不同行所必需的。它们唯一地标识表中的每一行数据。
另一方面,外键是另一个表的主键。因此,通过关联主键和外键,数据库关系得以保持。
**对于一对多,“一”方包含主键,“多”方包含该主键作为其外键。在上面的例子中,为了获得属于一张发票的所有行,我们查询 InvoiceLines 表,其中外键等于特定的发票号。
**对于多对多,通过使用称为“联接”表的第三个表,关系被分解为两个一对多关系。例如,我们的部分付款系统将发票表、付款表和 InvoicePayments 表作为连接表。InvoicePayments 表的主键将是一个组合键,由 Invoices 和 Payments 表的主键组成,如下所示
用于实施部分发票付款的表
请注意,连接表不包含任何其他数据,因为除了连接发票和付款表之外,它没有任何其他用途。
为了获得某个支付交易支付的发票,比如说PAY 1
,我们通过连接表连接发票和支付表,并查询payment_id = “PAY 1”
。
这就是数据库的基础知识。我们现在准备设计我们的数据库。
打个简单的比方,DBMS 之于 Excel 程序,数据库之于 Excel 工作簿,表格之于 Excel 工作表。
设计我们的 Xero 实现
现在我们对 Xero 有了基本的了解,可以开始创建它的数据库结构的草图了。请注意,我将使用Table_Name
格式。这是由下划线分隔的首字母大写单词。我还将对表名使用复数名称。
对于销售周期,我们将有以下表格
- 发票
- 客户 —一个客户可以有多张发票,但一张发票不能属于多个客户
- Invoice_Payments —记住我们现在的假设,Invoice_Payments 和 Invoices 分别是一对多的关系(没有部分付款)
- Invoice_Lines —这是发票和 COA 之间的连接表。一个账户可以出现在多张发票中,一张发票可以有多个账户。
- 会计科目表(COA)
对于购买周期,我们将有以下表格
- 票据
- 供应商 —一个供应商可以有多个票据,但是一个票据不能属于多个供应商
- Bill_Payments —记住我们现在的假设,Bill_Payments 和 Bill 之间分别是一对多的关系
- Bill_Lines —这是票据和 COA 的连接表。一个账户可以出现在多张票据中,一张票据可以有多个账户。
- COA —与销售周期中的上述内容相同。这里只是为了完整性。
对于现金周期,我们将有下面的表(我们已经在上面创建的付款表)
- Received _ money——可能有一个可选客户
- Received_Money_Lines —这是 Received_Money 和 COA 之间的连接表
- spend _ money—可能有一个可选的供应商
- 支出款项行 —这是支出款项和 COA 之间的连接表
从概念上讲,我们的数据库结构如下
会计数据库模型
这个图用数据库的说法叫做实体关系图或者 ERD 。一对多关系由1-M指定,多对多关系由M-M指定。
上图中没有显示连接表,因为它们隐含在具有多对多关系的表中。
用 SQL 实现我们的版本
现在是时候用 SQL 实现我们的模型了。我们先从定义一些约定开始。
主键的字段/列名为id
,外键的格式为table_id
,其中table
是单数形式的“多”方的表名。例如,在发票表中,外键将是customer_id
。
SQL 代码的时间到了。在这里。
*DROP TABLE IF EXISTS `COA`;CREATE TABLE IF NOT EXISTS `COA` (
id INTEGER PRIMARY KEY,
name TEXT
);DROP TABLE IF EXISTS `Customers`;CREATE TABLE IF NOT EXISTS `Customers` (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
contact_person TEXT,
email TEXT,
phone TEXT,
fax TEXT,
address TEXT
);DROP TABLE IF EXISTS `Invoice_Payments`;CREATE TABLE IF NOT EXISTS `Invoice_Payments` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
description TEXT,
reference TEXT,
total DECIMAL(20,2) NOT NULL,
coa_id INTEGER NOT NULL, -- automatically Bank
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Invoices`;CREATE TABLE IF NOT EXISTS `Invoices` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
due_date DATE,
description TEXT,
reference TEXT,
total DECIMAL(10,2) NOT NULL,
status BOOLEAN,
customer_id INTEGER,
invoice_payment_id INTEGER,
coa_id INTEGER NOT NULL, -- automatically AR
FOREIGN KEY(`customer_id`) REFERENCES `Customers`(`id`),
FOREIGN KEY(`invoice_payment_id`) REFERENCES `Invoice_Payments`(`id`),
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Received_Moneys`;CREATE TABLE IF NOT EXISTS `Received_Moneys` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
description TEXT,
reference TEXT,
total DECIMAL(20,2) NOT NULL,
customer_id INTEGER,
coa_id INTEGER NOT NULL, -- automatically Bank
FOREIGN KEY(`customer_id`) REFERENCES `Customers`(`id`),
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Invoice_Lines`;CREATE TABLE IF NOT EXISTS `Invoice_Lines` (
id INTEGER PRIMARY KEY,
line_amount DECIMAL(20,2) NOT NULL,
invoice_id INTEGER,
line_coa_id INTEGER NOT NULL,
FOREIGN KEY(`invoice_id`) REFERENCES `Invoices`(`id`),
FOREIGN KEY(`line_coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Received_Money_Lines`;CREATE TABLE IF NOT EXISTS `Received_Money_Lines` (
id INTEGER PRIMARY KEY,
line_amount DECIMAL(20,2) NOT NULL,
received_money_id INTEGER,
line_coa_id INTEGER NOT NULL,
FOREIGN KEY(`received_money_id`) REFERENCES `Received_Moneys`(`id`),
FOREIGN KEY(`line_coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Suppliers`;CREATE TABLE IF NOT EXISTS `Suppliers` (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
contact_person TEXT,
email TEXT,
phone TEXT,
fax TEXT,
address TEXT
);DROP TABLE IF EXISTS `Bill_Payments`;CREATE TABLE IF NOT EXISTS `Bill_Payments` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
description TEXT,
reference TEXT,
total DECIMAL(20,2) NOT NULL,
coa_id INTEGER NOT NULL, -- automatically Bank
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Bills`;CREATE TABLE IF NOT EXISTS `Bills` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
due_date DATE,
description TEXT,
reference TEXT,
total DECIMAL(10,2) NOT NULL,
status BOOLEAN,
supplier_id INTEGER,
bill_payment_id INTEGER,
coa_id INTEGER NOT NULL, -- automatically AP
FOREIGN KEY(`supplier_id`) REFERENCES `Suppliers`(`id`),
FOREIGN KEY(`bill_payment_id`) REFERENCES `Bill_Payments`(`id`),
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Spent_Moneys`;CREATE TABLE IF NOT EXISTS `Spent_Moneys` (
id INTEGER PRIMARY KEY,
tran_date DATE NOT NULL,
description TEXT,
reference TEXT,
total DECIMAL(20,2) NOT NULL,
supplier_id INTEGER,
coa_id INTEGER NOT NULL, -- automatically Bank
FOREIGN KEY(`supplier_id`) REFERENCES `Suppliers`(`id`),
FOREIGN KEY(`coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Bill_Lines`;CREATE TABLE IF NOT EXISTS `Bill_Lines` (
id INTEGER PRIMARY KEY,
line_amount DECIMAL(20,2) NOT NULL,
bill_id INTEGER,
line_coa_id INTEGER NOT NULL,
FOREIGN KEY(`bill_id`) REFERENCES `Bills`(`id`),
FOREIGN KEY(`line_coa_id`) REFERENCES `COA`(`id`)
);DROP TABLE IF EXISTS `Spent_Money_Lines`;CREATE TABLE IF NOT EXISTS `Spent_Money_Lines` (
id INTEGER PRIMARY KEY,
line_amount DECIMAL(20,2) NOT NULL,
spent_money_id INTEGER,
line_coa_id INTEGER NOT NULL,
FOREIGN KEY(`spent_money_id`) REFERENCES `Spent_Moneys`(`id`),
FOREIGN KEY(`line_coa_id`) REFERENCES `COA`(`id`)
);*
这里有几件事:
- SQL 命令不区分大小写,
CREATE TABLE
与create table
相同 IF EXISTS
和IF NOT EXISTS
是可选的。我只是用它们来防止我的 SQL 命令中的错误。例如,如果我删除一个不存在的表,SQLite 会给出一个错误。此外,我将IF NOT EXISTS
放在 create table 命令上,这样我们就不会意外地覆盖任何现有的表。- 小心使用
DROP TABLE
命令!它将删除一个现有的表,而不发出警告,即使它有内容。 - 表名也可以全部大写或不大写。如果表名有空格,应该用反斜杠(
)括起来。它们不区分大小写。
SELECT * FROM Customers与
select * from customers.`相同
尽管 SQL 在语法方面有些宽松,但是您应该努力保持 SQL 代码一致性。
还要注意上面 ERD 中显示的关系。还要记住,外键位于多侧。
顺序很重要,因为由于外键的存在,一些表会成为另一个表的依赖项。例如,您必须先在发票表之前创建 first Invoice_Payments,因为前者是后者的依赖项。这里的技巧是从 ERD 的边缘开始,因为这些边缘具有最少的外键。
您还可以下载一个 SQLite 中的示例数据库,这个链接中没有任何内容。
要查看它,您可以使用免费的开源 SQLite 浏览器。在这里下载!
向我们的数据库添加内容
现在我们有了示例数据库,让我们向它输入数据。样本数据可以从这里下载——只需根据需要将其分解为 CSV。
请注意,学分显示为正数,学分显示为负数。
对于这篇文章,我只是使用了 DB Browser 的导入功能从上面的 Excel 文件中导入 CSV。例如,导入 Customers.csv
- 选择“客户”表
- 转到文件>导入> CSV 文件中的表格,并选择客户. csv**
- 对于所有后续提示,单击“确定/是”以导入数据。
如果您发出下面的 SQL 命令,它应该显示我们数据库中的所有客户
从我们的数据库创建财务报告
为了证明我们的数据库作为一个粗略的会计系统工作,让我们创建试算平衡表。
第一步是为我们的发票、账单、Received _ Moneys 和 Spent _ Moneys 事务创建事务视图。代码将如下所示
*DROP VIEW IF EXISTS Invoice_Trans;CREATE VIEW IF NOT EXISTS Invoice_Trans ASwith recursiveitrans as (SELECT
'INV'||i.id as `tran_id`,
i.tran_date,
i.coa_id as ar_account,
-- ABS(total) as `total`,
'Accounts Receivable' as `coa_name`,
i.total,
il.id as `line_id`,
il.line_coa_id,
il.line_amount,
ip.id,
ip.coa_id as bank_account,
'Business Bank Account' as `bank_name`,
i.status
from Invoices as i
left join Invoice_Lines as il on i.id = il.invoice_id
left join COA as c on i.coa_id = c.id
left join Invoice_Payments as ip on i.invoice_payment_id = ip.id
)select
itrans.*,
c.name as `line_coa_name`
from itrans
left join COA as c on itrans.line_coa_id = c.id;SELECT * from Invoice_Trans;********************************************************************DROP VIEW IF EXISTS Bill_Trans;CREATE VIEW IF NOT EXISTS Bill_Trans ASwith recursivebtrans as (SELECT
'BILL'||b.id as `tran_id`,
b.tran_date,
b.coa_id as ap_account,
-- ABS(total) as `total`,
'Accounts Payable' as `coa_name`,
b.total,
bl.id as `line_id`,
bl.line_coa_id,
bl.line_amount,
bp.id,
bp.coa_id as bank_account,
'Business Bank Account' as `bank_name`,
b.status
from Bills as b
left join Bill_Lines as bl on b.id = bl.bill_id
left join COA as c on b.coa_id = c.id
left join Bill_Payments as bp on b.bill_payment_id = bp.id
)select
btrans.*,
c.name as `line_coa_name`
from btrans
left join COA as c on btrans.line_coa_id = c.id;SELECT * from Bill_Trans;********************************************************************DROP VIEW IF EXISTS Received_Money_Trans;CREATE VIEW IF NOT EXISTS Received_Money_Trans AS
SELECT
'RM'||rm.id as `tran_id`,
tran_date,
coa_id,
'Business Bank Account' as `coa_name`,
total,
rml.id as `line_id`,
rml.line_coa_id,
c.name as `line_coa_name`,
rml.line_amount
from Received_Moneys as rm
left join Received_Money_Lines as rml on rm.id = rml.received_money_id
left join COA as c on c.id = rml.line_coa_id;SELECT * from Received_Money_Trans;********************************************************************DROP VIEW IF EXISTS Spent_Money_Trans;CREATE VIEW IF NOT EXISTS Spent_Money_Trans AS
SELECT
'SM'||sm.id as `tran_id`,
tran_date,
coa_id,
'Business Bank Account' as `coa_name`,
total,
sml.id as `line_id`,
sml.line_coa_id,
c.name as `line_coa_name`,
sml.line_amount
from Spent_Moneys as sm
left join Spent_Money_Lines as sml on sm.id = sml.spent_money_id
left join COA as c on c.id = sml.line_coa_id;SELECT * from Spent_Money_Trans;*
在前两条语句中,我使用了 CTE(带有关键字recursive
)。cte 很有用,因为我将 4 个表组合在一起,以获得发票交易和相应付款的单一视图。你可以在 SQLite 这里了解更多关于 CTEs 的内容。
执行上述命令后,您的数据库应该有以下 4 个视图。
交易视图
最后,我们创建试算平衡表(简称 TB)的代码。请注意,TB 只是我们的交易余额的集合,记录了我们在设计数据库时制定的规则。
代码如下所示
*DROP VIEW IF EXISTS Trial_Balance;
CREATE VIEW IF NOT EXISTS Trial_Balance as
-- CREATE TB
-- select all sales
select
line_coa_id as acct_code,
line_coa_name as acct_name,
(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as debit_bal,
(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as credit_bal
from Invoice_Trans
group by line_coa_id-- select all purchases
union allselect
line_coa_id as acct_code,
line_coa_name as acct_name,
(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as debit_bal,
(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as credit_bal
from Bill_Trans
group by line_coa_id-- select all received money
union allselect
line_coa_id as acct_code,
line_coa_name as acct_name,
(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as debit_bal,
(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as credit_bal
from Received_Money_Trans
group by line_coa_id-- select all spent money
union allselect
line_coa_id as acct_code,
line_coa_name as acct_name,
(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as debit_bal,
(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as credit_bal
from Spent_Money_Trans
group by line_coa_id-- select all AP
union allselect
ap_account as acct_code,
coa_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Bill_Trans
where status = "0"-- select all AR
union allselect
ar_account as acct_code,
coa_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Invoice_Trans
where status = "0"-- select all bill_payments
union allselect
bank_account as acct_code,
bank_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Bill_Trans
where status = "1"-- select all invoice_payments
union allselect
bank_account as acct_code,
bank_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Invoice_Trans
where status = "1"-- select all received_money
union allselect
coa_id as acct_code,
coa_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Received_Money_Trans-- select all spent_money
union allselect
coa_id as acct_code,
coa_name as acct_name,
-(case when sum(line_amount) < 0 then sum(line_amount) else 0 end) as debit_bal,
-(case when sum(line_amount) > 0 then sum(line_amount) else 0 end) as credit_bal
from Spent_Money_Transorder by acct_code*
上面的代码包含由命令union all
连接的多个 SQL 查询。我已经对每个查询进行了注释,以显示每个查询试图实现的目标。
例如,第一个查询试图获取发票交易的所有信用(主要是销售额)。第二个用于账单交易(主要是购买)的借项等等。
执行它应该会产生下面的 TB。
你可以把它放入 Excel 来检查借贷是否相等(我就是这么做的)。借项和贷项合计分别为 14115 和-14115。
万岁!你成功了
创建一个会计系统是非常复杂的。我们基本上探索了数据库设计的整个领域——从概念到 ERD 到创建再到查询。为自己走到这一步感到自豪。
请注意,我们故意限制了我们的数据库,以便更多地关注概念。你可以解除这些,并尝试建立另一个没有限制。
就是这样!你现在是 SQL 忍者了!恭喜你!
Ian Stauffer 在 Unsplash 上拍摄的照片
查看我的第二本书 会计数据库设计 即将在 Leanpub 上出版!
还我第一本书 PowerQuery 熊猫指南 上Leanpub。**