如何使用 Python 将您的数据科学项目轻松部署为 web 应用程序
教程+在 Heroku 中部署——Streamlit 带来了革命性的变化,使部署 web 应用程序变得更加容易。
Photo by Christina @ wocintechchat.com on Unsplash
你知道在网上部署应用程序在技术上有多复杂。借助 Streamlit,您可以使用纯 python 创建应用程序,无需担心前端开发。在本地服务器上实现它只需要一个命令。如果你想在网络上使用它,你可以在任何其他云平台上托管它。
Streamlit — Pure Python
在本教程中,您将使用 python 和 streamlit 构建数据可视化应用程序。后来我们把它部署在 Heroku 供公众使用。这是我们将在 Heroku 中构建和部署的一个预告片。
设置简化 it
我们用 pip 安装 Streamlit,如下所示:
pip install streamlit
运行 Streamlit 就像运行一样简单:
streamlit run XXX.py
现在,我们还没有创建我们的应用程序。但是在我们继续之前,我们创建了一个 GitHub 存储库,我们稍后将使用它在 Heroku 中部署应用程序。创建 Github 存储库后,克隆它。
git clone [https://github.com/shakasom/streamlit-medium.git](https://github.com/shakasom/streamlit-medium.git)
从现在开始,我们处理这个文件夹。所以转到这个文件夹:
cd streamlit-medium/
在下一节中,我们将开发一个 python 数据可视化应用程序。
本地应用程序开发
你可以使用你最喜欢的开发环境。让我们创建一个 python 文件来编写应用程序。你想叫它什么都可以。我会把我的叫做 app.py
我们首先导入我们需要的库。
import streamlit as st
import pandas as pd
import plotly_express as px
*import folium* from folium.plugins import HeatMap
import seaborn as sns
本教程的数据是 2016 年 1 月至 6 月华盛顿报道的犯罪。让我们创建一个函数来帮助我们做到这一点。
# Get the data from url and request it as json file@st.cache(*persist*=True, *suppress_st_warning*=True)
*def* load_data():
df = pd.read_csv( “https://query.data.world/s/6joi7hjgjmwifhl2clpldwm36xmvmx")
df[“REPORTDATETIME”] = pd.to_datetime(
df[“REPORTDATETIME”], *infer_datetime_format*=True)
df[“Day”] = df[“REPORTDATETIME”].dt.day
df[“Month”] = df[“REPORTDATETIME”].dt.month
df[“Hour”] = df[“REPORTDATETIME”].dt.hour
return df
让我们创建 main 函数,当我们执行它时,它会自动运行我们的应用程序。首先,我们使用上面的函数获取数据,并将其命名为 df_data。我们还开始编写标题(st.header)并向我们的应用程序添加图像(st.image)。
*def* main():
df_data = load_data()
st.header(“Washington Crimes Data Exploration”)
st.subheader(“A demo on how to use Streamlit”)
st.image(image.jpg", *width*=600)if __name__ == “__main__”:
main()
有了这个简单而纯粹的 python 代码,我们可以在本地派生出一个 Streamlit 应用程序。你需要跑:
streamlit run app.py
还有瓦拉!您的应用将在浏览器中启动。让我们检查一下我们目前掌握的情况。一个标题,副标题文本和一个图像,你可以从下面的截图看到。它运行在本地主机端口 8501 上。
Streamlit screenshot
让我们为我们的应用程序添加更多的功能。我们将逐步做到这一点。我们首先在应用程序中显示数据集的一些行。我们还展示了整个数据集的形状。要控制显示项目,可以使用 st.checkbox ,点击一次;它将显示缩进代码中的内容。 st.write 可以拿熊猫做手术,所以如果你想展示头部,我们可以这样做。
if st.checkbox(“show first rows of the data & shape of the data”):
st.write(df_data.head())
st.write(df_data.shape)
在主函数中添加上述代码并保存。现在,如果您转到浏览器并刷新或重新运行您的应用程序,您将看到一个复选框,如果您单击它,您将看到数据集的前几行。
Adding checkbox
让我们在应用程序中添加一张地图。首先,我们创建一个函数来返回一个图形。
@st.cache(*persist*=True, *suppress_st_warning*=True)
*def* display_map(*df*):
st.subheader(“ Displaying Point based map”)
px.set_mapbox_access_token("MAPBOX TOKEN”)
fig = px.scatter_mapbox(df, *lat*=”Y”, *lon*=”X”, *color*=”METHOD”\
, *zoom*=10)return fig
在主函数内部,我们可以调出 st.plotly_chart 。有一些内置的图表功能,包括 Matplotlib、Bokeh 等,但任何其他尚未在 streamlit 中实现的库也是可能的,因为我们将在接下来使用 Folium 库。
st.plotly_chart(display_map(df_data))
上面的代码将在我们的应用程序中添加一个美丽的 Plotly 地图,你可以从下面的截图中看到。
Streamlit app
侧栏元素
如果您想使用侧边栏,您可以将 st.sidebar 与任何其他 streamlit 功能一起使用。该方法将在侧边栏中创建您的应用程序元素。让我们看一个使用带有下拉选择的侧边栏的例子— selectbox。
首先,我们创建选择—热图和计数图。
dataviz_choice = st.sidebar.selectbox(“Choose Data Visualization”, [“None”, “Heatmap”, “Countplot”])
让我们为热图创建一个函数。
*def* heat_map(*df*):
locs = zip(df.Y, df.X)
m = folium.Map([38.8934, -76.9470], *tiles*=’stamentoner’, *zoom_start*=12)
HeatMap(locs).add_to(m)
return st.markdown(m._repr_html_(), *unsafe_allow_html*=True)
注意,这里我们使用的是 st.markdown。在 Streamlit 中还没有实现 yellow 库,但是我们可以在上面的代码中以这种方式创建一个 yellow map。
现在,让我们用热图和计数图填写我们的选择。
if dataviz_choice == “Countplot”:
st.subheader("Countplot")
sns.countplot(“METHOD”, *data*=df_data)
st.pyplot()elif dataviz_choice == “Heatmap”:
st.subheader(“Heat Map”)
heat_map(df_data)
如果您在浏览器中重新运行您的应用程序,您应该会看到一个带有选项的侧边栏。如果我们选择热图,我们的应用程序将如下所示。
Streamlit Heatmap
在下一节中,我们将在 Heroku 中部署应用程序,在那里您可以免费构建应用程序。如果你愿意,你也可以用 AWS 或者 Google 云平台来部署,但是在本教程中,我将使用 Heroku。
在 Heroku 部署应用程序
首先,确保你在 Heroku 注册。然后,我们需要创建四个文件来在云中部署我们的应用程序:Procfile、config 文件、需求和运行时。让我们首先创建 Procfile。当我们想在 Heroku 中剥离应用程序时,我们在这里提供要运行的内容。以下是 Procfile 的外观:
web: sh create_config.sh && streamlit run app.py
保存文件并将其命名为“Procfile ”,不带任何扩展名。
接下来,我们创建配置文件,将其命名为 create_config.sh,并将以下代码粘贴到其中。
mkdir -p ~/.streamlit
echo “[server]
headless = true
port = $PORTenableCORS = false
” > ~/.streamlit/config.toml
需求文件包含了应用程序需要的库。您可以使用 pip 冻结。requirements.txt 文件如下所示:
streamlit==0.51.0
pandas==0.25.2
plotly-express==0.4.1
seaborn==0.9.0
folium==0.10.0
最后,运行时文件指定您希望应用程序运行的 python 版本。
python-3.7.5
现在我们需要将这些新文件推送到我们的 Github 库。
git add *
git commit -m “deploying files”
git push
我们的本地文件现在被推送到我们的 Github 库,我们可以开始在 Heroku 部署应用程序。在命令行中运行以下命令,然后按任意键。
heroku login
将弹出一个新的浏览器。用你的证件登录 Heroku。一旦你这样做了,回到你的命令行,创建一个这样的 Heroku 应用程序。
heroku create streamlit-medium
最后,我们需要推动 Heroku。
git push heroku master
如果你现在逃跑
heroku open
您的应用程序将在新的浏览器中打开,或者您可以单击提供的 URL。也就是说,你可以与任何人共享这个 URL,他们将从任何地方访问你的应用程序。
Streamlit app deployed in Heroku
结论
在本教程中,我们用 python 和 streamlit 创建了一个数据可视化应用程序。我们还使用 Heroku 在云中部署了应用程序。本教程的代码可以在这个 Github 资源库中找到
你知道他们在网上部署应用程序在技术上有多复杂。有了 Streamlit,您可以用纯粹的…
github.com](https://github.com/shakasom/streamlit-medium)
您可以通过以下 URL 访问 Heroku web 应用程序:
[## 细流
编辑描述
streamlit-medium.herokuapp.com](https://streamlit-medium.herokuapp.com/)
如何将您的机器学习 Web 应用部署到数字海洋
使用 Fast.ai、Docker、GitHub 和 Starlette ASGI 框架
Y 你已经收集了你的数据,勤奋地清理了这些数据,把它们塞进你精心微调的模型中,并花费了大量的 GPU 时间来训练这个模型。预测是最先进的!太棒了。
但是现在呢?
当然是和全世界分享啦!它有如此巨大的潜力,以前没有人这样做过,你想让每个人都尝试一下!怎么会?你问。
在本教程中,我将向您介绍一种部署训练有素的机器学习模型的经济而灵活的方法。我将带你走完这一过程中的每一步,希望在读完这篇文章后,你在向世界部署你的“下一个大东西(模型)”时不会有任何问题。
如何训练(和输出)你的龙(模型)
Image from https://www.facebook.com/HowToTrainYourDragon/
首先,你需要训练你的模型,并导出它。在本文中,我们将使用 Fast.ai 的库来展示它是如何完成的。你可能想参考我的关于如何收集数据和训练一个中国书法分类器模型的两篇文章或者你也可以使用你自己的模型。出于本文的目的,我将假设您已经训练了模型并达到了您想要的准确率。
Fast.ai 使用一个learn
对象来训练模型,为了导出你的模型,使用方法learn.export()
将你训练好的模型导出并保存到一个export.pkl
文件中(上面 链接 的我的模型导出文件大约是 100MB* )。保存这个文件,我们以后会用到它。*
GitHub 驱动的网络开发
模型准备好了,下一步是 web 应用程序开发。我假设你是一个全栈的 web 开发人员,所以让我们直接进入编码。不,我只是在开玩笑。我们将使用 GitHub 上的样板 web 应用程序模板来快速准备好您的 web 应用程序。你只需要做一些小的调整,你就可以开始了。如果你不知道 GitHub 是什么,它是存放大量开源应用程序源代码的地方。我已经把一个现成的 web 应用程序的代码放在那里了,所以你可以很容易地下载和重用。
进入这个 GitHub 库,点击右边绿色大按钮克隆或者下载,如下图:
在弹出窗口中,复制链接,然后转到您的终端并键入:
git clone [https://github.com/wayofnumbers/fastai-vision-uvicorn-gunicorn-starlette-docker.git](https://github.com/wayofnumbers/fastai-vision-uvicorn-gunicorn-starlette-docker.git)
cd fastai-vision-uvicorn-gunicorn-starlette-docker
这些命令将把所有需要的代码克隆到你的本地机器上,在一个名为fastai-vision-uvicorn-gunicorn-starlette-docker
的文件夹下,并进入那个文件夹。这是我们将要处理的主文件夹,其中有几个值得解释的内容:
app :这个app
文件夹的结构如下:
template
|--app.html
main.py
export.pkl
这是您的 Starlette web 应用程序源代码所在的位置。它有一个非常简单的 Python 文件main.py
。 Starlette 是一个轻量级的 ASGI 框架/工具包,非常适合构建高性能的 asyncio 服务。
它还有保存的模型文件export.pkl
。template
文件夹中有一个 HTML 模板文件app.html
,它将作为你的 web 应用程序 UI。
还记得你保存的导出的export.pkl
文件吗?把那个拿出来,替换掉这个app
文件夹里的那个。所以应用程序会使用你的模型。也欢迎您更新 app.html 文件以获得更好的 UI,但是就部署而言,这不是必需的。现在你的 web 应用程序的源代码已经准备好了,我们需要把它打包到一个 Docker 容器中,并做一些测试。我们使用 Dockerfile 作为配置文件。我们将在下一节中探索更多。
让我们把它记录下来!
我们将使用 Docker 创建一个运行我们 web 应用程序的容器。如果你不知道 Docker 是什么,只知道它是一种迷你虚拟机,安装了所有必要的库和依赖项,因此应用程序可以平稳运行。它比真正的虚拟机更小、更灵活,并且可以非常容易地创建和部署。
首先,你需要安装 Docker。这里是一个非常全面的指南,供你参考。安装后,如果您运行的是 Ubuntu,那么运行以下命令会有所帮助:
sudo groupadd docker
sudo usermod -aG docker $USER
这将消除每次输入 docker 命令时使用sudo
的需要。重新启动,现在 docker 应该安装正确。
在app
文件夹和Dockerfile
所在的同一个目录中,我们需要创建一个 docker 映像,其中包含这个文件夹中的所有源代码,这样我们就可以进行测试了。输入以下命令(不要忘记“.”结尾):
docker build -t test_app .
这将根据Dockerfile
启动一个 docker 映像构建过程。这需要一段时间,所以让我们简单看看Dockerfile
里面有什么:
#1 FROM tiangolo/uvicorn-gunicorn-starlette:python3.7
#2 RUN pip install fastai aiohttp
#3 RUN pip install jinja2
#4 RUN pip install starlette
#5 COPY ./app /app
#6 WORKDIR /app
#7 EXPOSE 80
这是不言自明的:
**第 1 行:**指定我们将从哪个起始映像构建 docker 映像。我们用tiangolo/uvicorn-gunicorn-starlette:python3.7
。你可以在这里找到它的 GitHub 链接,在这里找到 Docker Hub 链接。
**2、3、4 行:**安装 fast.ai 库、jinja 模板框架、Starlette 框架等实用工具。
第 5 行:把你的应用文件夹复制到 docker 镜像中,这样我们的应用就可以在 docker 容器中运行了。
**第 6、7 行:**将工作目录分配给app
文件夹,将端口 80 暴露给外部,这样我们就可以通过端口 80(HTTP)访问 web app。
一旦创建了 docker 图像,运行docker images
进行检查。您会发现类似这样的内容:
REPOSITORY TAG IMAGE ID CREATED SIZE
test_app latest xxxxxxxxx 1 minutes ago 4.05GB
现在,我们可以从创建的映像启动 docker 容器,并在本地测试您的应用程序:
docker run -p 80:80 \
-v ./absolute/path/to/export.pkl:/app/export.pkl \
-e TITLE="Chinese Calligraphy Classifier" \
-e SUBTITLE="Can disambiguate Chinese calligraphy styles like KaiShu, LiShu, XiaoZhuan"
test_app
在上面的 docker 命令中,我们将端口指定为 80。我们将两个环境变量转移到容器中,TITLE
和SUBTITLE
,它们将用于显示我们的 web 应用程序 UI 标题。最后,我们指定了 docker 图像名:test_app
。请注意,对于export.pkl
文件,需要使用绝对路径,否则 Docker 将无法找到。
如果您没有看到任何错误,您的 docker 容器现在应该已经启动并运行了。走向你的浏览器,输入127.0.0.1
并点击回车,瞧!你应该看看网络应用。给它一个’楷书’、'隶书’或’小篆’的书法形象,打’分门别类【T27’,你应该看到这样的东西:
Very rough web app UI
你可以看到 app 把这个归类为‘凯叔’,没错。现在你的应用已经在本地机器上运行了,我们已经完成了 80%。剩下的就是部署到云上了。接下来让我们向云进发吧!
下一步,云!
对于云托管服务,我们将使用数字海洋。与亚马逊 AWS、GCP 或 Azure 等现有公司相比,它对开发者更友好,也更便宜。你可以按照这篇写得很好的简明教程来创建一个你自己的账户和‘Droplet’。(“Droplet”是一个由 Digital Ocean 运行的虚拟机,你可以在其中安装你的应用程序,很像一个 AWS 实例。)如果您愿意,您可以使用此链接创建您的帐户,并免费获得 50 美元的信用点数,这足以让您入门。使用以下配置作为参考:
建议您创建至少有 4G 内存的 Droplets,因为安装 PyTorch 需要大量内存。以后你可以把它缩小到 2G。
您可以选择默认的“数据中心”并设置您的身份验证方法。使用 SSH 密钥或密码,无论哪种方式您都觉得更舒服。我个人比较喜欢 SSH 键,按键少,更安全。一旦创建了 Droplet,SSH 进入它,我们就为最后的部署做好了准备!
部署!部署!部署!
现在你应该能够以root
的身份 SSH 到你的服务器。建议创建一个拥有sudo
权限的普通用户,可以按照本教程进行操作。一旦创建了一个普通用户,注销您的root
用户,用您的普通用户帐户通过 SSH 重新登录到服务器。最终的部署与我们已经在本地机器上完成的非常相似,只是这次我们是在远程 droplet 服务器上完成的。首先,让 git 克隆我们的 repo,这样我们就有了源代码:
git clone [https://github.com/wayofnumbers/fastai-vision-uvicorn-gunicorn-starlette-docker.git](https://github.com/wayofnumbers/fastai-vision-uvicorn-gunicorn-starlette-docker.git)
cd fastai-vision-uvicorn-gunicorn-starlette-docker
不要忘记复制你的export.pkl
文件来替换app
文件夹中的内容。(如果您不知道如何操作,请点击此链接
如果没有安装 docker,请安装 docker。然后使用下面的命令构建 docker 映像。同样,如果由于内存不足而导致映像构建失败,请调高内存大小,稍后您可以调低内存大小,而不会增加太多成本。
# make it so that you don't need sudo for running docker command
sudo groupadd docker
sudo usermod -aG docker $USER# build the image
docker build -t test_app .
一旦构建了映像,启动 docker 容器:
docker run -p 80:80 \
-v ./absolute/path/to/export.pkl:/app/export.pkl \
-e TITLE="Chinese Calligraphy Classifier" \
-e SUBTITLE="Can disambiguate Chinese calligraphy styles like KaiShu, LiShu, XiaoZhuan"
test_app
一旦 docker 容器启动并运行,进入你的浏览器,输入你的 Droplet 的 IP 地址,点击回车。恭喜你!你已经成功地将你的深度学习模型部署到互联网上了!
结论
没那么难吧。使用标准 DigitalOcean Droplet 进行部署提供了很多灵活性。你可以对你的 Droplets 做任何你想做的事情,因为你有 root 权限。你可以在上面运行多个应用程序,只需支付很少的费用(5-10 美元应该足够了)。如果你的应用获得了一些牵引力,需要更多的资源,你可以很容易地扩大规模。
我希望这篇教程对你部署你的 AI 应用有所帮助。如果您有任何问题或想要分享您的部署经验,请在下面写下回复。部署愉快!
更新:我已经在这里部署了 web 应用。我使用 DigitaOcean 上的 web 应用程序作为 API 后端服务器,并将另一个基于 ReactJS 的 web 应用程序托管在 GitHub 页面上作为前端。由于 React 应用程序需要访问“http”URL,一些浏览器会显示“混合内容错误”,只需启用它,您应该可以自己尝试一下。也欢迎大家查看回购中的 React app 源代码。
欢迎任何反馈或建设性的批评。你可以在 Twitter @lymenlee 或者我的博客网站wayofnumbers.com上找到我。
如何使用 Heroku 将您的网站部署到自定义域
这篇博客记录了使用 Heroku 和 NameCheap 将一个用 Python 和 Flask 框架编写的网站部署到一个定制域所需的步骤。Flask 是一个微框架,它允许我们在后端使用 Python 与 HTML/CSS 或 Javascript 中的前端代码进行交互,以构建网站。人们也为此使用其他框架,比如 Django,但是我最喜欢的是 Flask,因为它很容易上手。
Having your website can help you stand out
一个小 Flask 网站可以通过创建一个新的 repo 来创建,通过创建一个 Python 文件,如 this :
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"**if** __name__ == **"__main__"**:
app.run(port=5000)
一个典型的 Flask 应用程序有文件夹static
存放所有的 CSS 和 JS 文件以及图片等。和一个用于 HTML 文件的templates
文件夹。我不会在这里说太多细节,但是在米格尔·格林伯格的教程中可以找到关于这方面的精彩信息。
当运行上面的文件python app.py
时,它会在本地打开网站,显示“Hello World!”用 HTML 写的。
我们如何将这个网站部署到一个自定义域,让每个人都可以看到?
1.创建一个 Heroku 帐户
去 Heroku 网站注册一个账号。Heroku 将允许我们将网站部署到以herokuapp.com
结尾的域中。我们将使用这个网站,并部署到自定义域。我们本可以使用亚马逊网络服务来部署网站,但是我发现这比使用像 Heroku 这样的解决方案要复杂得多。AWS 是基础设施即服务(IaaS),Heroku 是平台即服务(PaaS)。因此,我们不需要担心 Heroku 的具体基础设施细节!这使得它更容易使用。
现在,到这个链接并下载命令行的heroku
。如果您的机器上有自制软件,您可以使用brew install heroku/brew/heroku
将其安装到您的机器上。下次运行heroku login
输入您的 Heroku 凭证。
Heroku is used to deploy our local website to cloud
2.将您的应用部署到 Heroku
a)制作一个 Procfile:
要将你的 app 部署到 Heroku,首先创建一个Procfile
,保存在与app.py
相同的文件夹中。请注意,Procfile 的而没有任何扩展名,如txt
等。该 Procfile 将包含内容web: gunicorn app:app
该语句将允许 Heroku 运行 gunicorn 作为该应用程序的 web 服务器。确保你的虚拟环境中安装了gunicorn
。
b)制作 requirements.txt:
还有,用pip freeze > requirements.txt
把这个项目的所有需求写成一个文件。Heroku 将使用它在云中安装所有必要的软件包来运行您的应用程序。
c)【可选】Make runtime.txt:
如果你想指定 Heroku 应该在云端使用哪个 Python 版本来运行你的 app,在这个文件里就提为python-3.6.6
就可以了。
d)部署到 Heroku: 运行heroku create <app-name>
其中<app-name>
是你喜欢的名字。如果你的应用程序名称是calm-river
,那么 Heroku 会将你的网站部署到calm-river.herokuapp.com
。确保git
安装在你的命令行上。
运行git init
将当前文件夹初始化为 git 存储库。接下来,运行git add .
,然后运行git commit -m "first commit"
,将所有文件提交给git
。最后运行git push heroku master
将你的应用部署到 Heroku~
如果一切顺利,你可以在<app-name>.herokuapp.com
访问你的网站
3。链接到 Heroku 上的自定义域名:
接下来,我们需要从 NameCheap 或 GoDaddy 购买一个自定义域名。假设您购买了域名,example.com
接下来,运行heroku domains:add [w](http://www.calm-river.com)ww.example.com
将这个自定义域名添加到您的 Heroku 应用程序中。它会给你一个 DNS 目标类型和 DNS 目标名称。这个目标类型应该是CNAME
。我们需要将这个CNAME
记录链接到你域名的设置控制台。
4.将 CNAME 记录添加到您的自定义域:
进入 Namecheap 设置控制台,然后进入Advanced DNS
。将CNAME
记录添加到主机目标,其中Type
为CNAME
,Host
为www
,Value
为您将域添加到 Heroku 时收到的 DNS 目标名称。
5.将 URL 重定向记录添加到您的自定义域:
添加 CNAME 记录后,添加URL Redirect record
,主机为@
,设置Unmasked
为[http://www.example.com](http://www.example.com)
。
允许 30 分钟来传播这些设置,您的网站应该在您的自定义域上运行!希望这些步骤对你寻求与世界分享你的工作有用。更多细节可以在 Heroku 网站上找到这些步骤。
希望能看到你的网站转转!
**附注:我正在创建一个名为
“查询理解技术概述”**的新课程。
本课程将涵盖信息检索技术、文本挖掘、查询理解技巧和诀窍、提高精确度、召回率和用户点击量的方法。有兴趣的请在这里报名!这种兴趣展示将允许我优先考虑和建立课程。
My new course: Overview of Query Understanding Techniques
如果你有任何问题,请给我的 LinkedIn 个人资料留言,或者给我发电子邮件到 sanket@omnilence.com。感谢阅读!
如何利用机器学习检测恶意推文
因为现在一切都是令人讨厌的…
Don’t give me that attitude
我想我不需要提醒你们所有人,尽管互联网有很多好处,但它也可能是消极的温床。只要看看 Reddit 或 YouTube 或 eelslap.com,你就会明白我的意思。
Twitter 一直是一个平台,人们可以在这里公开世界上和生活中发生的一切。我们的总统极其善于利用这个平台表达自己的意见,甚至调节美国公众的普遍看法。你可以想象,鉴于 Twitter 每天有 1.26 亿用户,不幸的是,有些人在说出自己的想法时没有过滤器。缺乏过滤器的人导致非常不合适的推文,从而导致不可避免的争论和痛苦。
今天我们要找点乐子,看看我们能否自动过滤掉哪些推文是冒犯性的,哪些不是。
议程
- 数据预处理
- 数据清理
- 标记化
- 堵塞物
- 数据可视化
- 提取特征
- 模型结构
- 需要改进的地方
- 资源
如果你想更深入地了解这个项目,或者如果你想添加代码,可以查看一下 GitHub 库 。
数据预处理
像往常一样,在我们开始实际的细节之前,我们需要在做任何有趣的事情之前准备好我们的数据。所有必要的数据都在 GitHub 存储库中。
我们有两个文件,一个训练文件和一个测试文件。在火车文件中,我们有各种推文的随机分类。有三个特性,一个唯一的 id、一个标签和实际的 tweet 文本。如果推文是非冒犯性的(非种族主义或性别歧视等),标签为“0”。)而标签是‘1’如果推文很冒犯的话。
首先,我们将导入一大堆库。
现在我们将使用熊猫来读取测试和训练文件。
让我们用熊猫来看看我们的数据是什么样的。
我将使用一些更好的数据符号,只显示那些被标记为攻击性的推文。
现在我们已经加载了数据,我们可以通过清理数据来最大限度地提高机器学习的效率。
数据清理
对于数据清理,我们将采取三个步骤:
- 移除 Twitter 句柄
- 删除标点、数字和特殊字符
- 删除那些没有太大价值的小词
为了节省时间,我们将组合我们的训练和测试数据帧,并在两者上进行所有的数据清理。
现在,我们将创建一个正则表达式,允许我们从我们的推文中找出并删除 Twitter 句柄。
现在我们的组合数据框架应该不再有 Twitter 句柄了。我们可以用一个简单的脚本删除特殊字符、数字和标点符号。
然后我们将删除没有增加价值的短词。
让我们看看现在合并后的数据帧是什么样子。
Nice work!
标记化
真正的乐趣开始了!因为机器学习算法不能像我们一样找到模式和见解(它们不能只是阅读一整串文本,所以它们必须接受单个单词),我们需要一种方法来保留每条推文的*【本质】*,同时让算法尽可能容易地了解什么使推文具有攻击性或不具有攻击性。
幸运的是,我们有这种有用的预处理形式,叫做记号化,它基本上是把你所有的文本分割成最基本的单元。
这是一个看起来像什么的例子。
New York is indeed special
标记化实际上非常简单。你所要做的就是把你的文本分成独立的部分。
嘣!我们结束了!
堵塞物
既然我们已经完成了标记化,我们需要继续下一步的预处理步骤,称为词干,本质上是将每个标记化的单词转换成它的词根形式和时态。例如,对“创建”一词进行词干化会产生“创建”一词,而“寄售”一词会变成“寄售”。
让我们来看看我们的词干推文。
数据可视化
在我们进入机器学习之前,对我们的数据进行一些直觉是一个很好的实践。数据可视化是我们实际探索和理解数据的一种很好的方式。
我们想回答的一些问题是:
- 数据集中最常见的词是什么?
- 负面和正面推文最常用的词是什么?
- 一条推文中有多少标签?
- 相关的趋势有哪些?
常用词
为了看到我们推文中的常用词,我们可以创建一个词云来真正看到它们。
正面推文中的单词
负面推文中的词语
(警告:这个词云中的词绝对是 NSFW)
标签
我们故意不删除标签,因为它们可以携带大量有用的信息。我们将提取标签,看看哪些是最常出现的。
现在让我们画出最常见的标签。
Positive hashtags
Negative hashtags
提取特征
现在我们已经完成了所有必要的步骤,是时候真正开始机器学习了。
我们将使用逻辑回归机器学习算法来训练我们的预测模型。在我们实际训练我们的模型之前,我们必须将文本数据转换成算法实际可以读取的格式。
我们可以使用两种方法,词袋和 TF-IDF(术语频率-逆文档频率)。我不会详细介绍这些特征提取模型实际上是如何工作的,但是可以通过我放在本文底部的参考资料来了解更多信息。
首先我们将提取单词袋。
然后我们用 TF-IDF 提取。
现在,我们可以使用这些提取的数据框架来训练我们的逻辑回归模型。
模型结构
是时候建立我们的模型了。
首先,我们将建立单词袋数据框架。
现在让我们看看我们预测准确度的结果。
现在,我们将使用 TF-IDF 数据框架构建模型。
看来我们的模型还有很多需要改进的地方。尽管这是一个开始。
需要改进的地方
感谢您花时间通读这篇文章!请随意查看我的作品集网站或我的 GitHub 。
1.使用不同的 Twitter 数据。
2.尝试不同的自然语言处理方法
3.使用不同的机器学习算法
4.调整超参数
资源
如何检测人脸登录时的张嘴
你用过银行/支付 app 人脸登录,被要求张嘴、点头或转头吗?这种方法非常流行,特别是在中国,以防止使用静态人脸图像或 3D 打印进行欺骗/黑客攻击。我花了大约两天时间,终于想出了一个非常简单的方法,利用来自人脸识别项目的特征输出来检测嘴巴张开。
下面是将该算法应用于实时网络摄像头视频时的效果。
人脸识别项目
face_recognition 是一个基于 dlib 的非常棒的人脸识别开源项目,正如它自己所描述的:
世界上最简单的用于 Python 和命令行的面部识别 api
通过几行 Python 代码,您可以检测到一张脸,识别出是谁,并输出脸部特征,如下巴、眼睛、鼻子和嘴唇。
例如,对一张奥巴马图片运行示例find _ face _ features _ in _ picture . py(只有 40 行代码),可以得到如下所示的所有面部特征。
Source image
Facial features drawn in the image
您还将在标准输出中获得面部特征细节,包括上唇和下唇。每个特征都是位置点的列表。
Facial feature outputs
面部识别装置
你可以按照 github 上的 face_recognition 项目中的步骤,或者直接下载预配置的 VM (强烈推荐)。
检测嘴张开的算法
face_recognition 提供了用于静态图形分析的 API,但它不会告诉你像张嘴或点头这样的面部动作。然而,我们可以通过特征输出来检测这些运动,即,在这种情况下嘴张开/闭合。
上唇和下唇特征输出都包含 12 个位置点的列表,但顺序不同。
top_lip points: [(181, 359), (192, 339), (211, 332), (225, 336), (243, 333), (271, 342), (291, 364), (282, 363), (242, 346), (225, 347), (211, 345), (188, 358)]
bottom_lip points: [(291, 364), (270, 389), (243, 401), (223, 403), (207, 399), (190, 383), (181, 359), (188, 358), (210, 377), (225, 381), (243, 380), (282, 363)]
使用 matplotlib 绘制唇点,可以清楚地看到点的顺序,如下所示。感兴趣的话,点击查看剧情脚本。
Algorithm Illustration
我的算法很简单:
如果嘴巴张开高度大于嘴唇高度*比率,则嘴巴张开。
- 比率:可调,定义开口的大小。你可以简单地把 ratio = 1,也就是说嘴张开的程度大于唇高。
- 唇高:三对点距离的平均值如下:
2–10
3–9
4–8
这些对对于顶部和底部唇点都是相同的。
- 口高:三对点距离的平均值,如下:
Top lip 8–10 Bottom lip
Top lip 9–9 Bottom lip
Top lip 10–8 Bottom lip
算法代码
唇高功能:
如果你把每个点对的指数加起来,你会发现总和总是 12。所以如果 i 是一个点,另一个点就是 12-i.
两点(x1,y1)和(x2,y2)的距离定义如下。
因此我们得到下面的嘴唇高度函数。
口高功能:
将各口穴对的指数相加,总和为 18。
检查口开启功能:
使用唇高和嘴高功能,我们可以定义检查嘴张开功能如下。
我选择上嘴唇高度和下嘴唇高度的最小值作为嘴唇高度。或者可以用平均值。
而我挑一个比值为 0.5,就不用张大嘴测试了😄。
mouth_open_algorithm_part3.py
好了,一切都准备好了。让我们来测试一下。
mouth_open_algorithm_test.py
产出:
top_lip height: 12.35
bottom_lip height: 21.76
mouth height: 33.34
Is mouth open: True
真实世界的例子
结合 face_recognition 和嘴部张开检测算法,我们可以开发一个网络摄像头实时人脸识别应用程序,具有检测嘴部张开/闭合的能力,正如你在本文开头的视频中看到的那样。它只有 80 行代码,你可以从 github 这里克隆。注意,你需要先安装 face_recognition 包。
git clone [https://github.com/peterjpxie/detect_mouth_open.git](https://github.com/peterjpxie/detect_mouth_open.git)cd detect_mouth_openpython facerec_from_webcam_mouth_open.py
笔记
这只是检测嘴巴张开的一种方式。你肯定会从其他来源找到其他类似或不类似的方法,或者你可以找到自己的方法。在寻找答案之前,自己想出一个方法是有趣和令人兴奋的。
感谢阅读。
如何确定你的机器学习模型是否已经可以部署了?
它足够“准确”吗?
如果你已经为现实世界的产品建立了机器学习模型,你就会知道弄清楚一个模型何时准备好被部署是多么困难。数据科学家很乐意对这个问题进行迭代,也许是无限地迭代,以不断提高模型的准确性。但是你如何确定它是否好到可以发布呢?有四种方法可以为可部署的模型设置精度目标:
Image by rawpixel from Pixabay
基于基线:
机器学习中的基线是解决问题的简单/启发式方法。您可以将预测模型的精度目标设置为比基线方法的精度提高一定的百分比。当您迭代您的模型时,您可以将每个部署的模型版本的准确性视为更新的基准,它需要在您的模型的后续版本中进行维护或改进。
例子:你的模型预测了用户对一部电影的评价。基线方法可以是使用基于所有其他用户评级的同一电影的平均/中间评级作为对该用户的预测。基于这一计算,您可以评估您的模型与基线相比的表现如何,并设定一个目标,即您的预测模型在相关指标方面必须比基线方法好 5%,才能准备好进行部署。
基于使用:
模型的准确性会影响产品的用户。建立准确度目标的一种方法是根据用户工作流程确定可接受的误差对用户的影响。
**示例:**您的模型决定了一个事件建议应用程序一天中发送给用户的建议的理想数量。现在要为模型设置一个准确性目标,您可以确定一个阈值,该阈值对于用户来说是可接受的,可以认为建议是有效的,并且不会给用户带来通知疲劳。这样,根据您的产品类型,您可以执行用户工作流研究和产品测试,以确定可接受的误差阈值,从而为您的模型准确性设定目标。
业务基础:
每个产品都解决一个商业问题。基于您的目标业务 KPI,您可以估计模型的准确性目标,以满足业务目标。
**举例:**你的产品声称通过基于机器学习模型的更有针对性的广告获得了更高的广告点击率。你宣称的目标是每天每显示 500 个广告获得 15 次点击,即 3%的点击率。为了能够达到这个目标,您可以计算您的模型需要有多精确,当模型精确时,考虑预期的用户可操作性。
基于安全:
根据行业和模型应用的不同,您的产品可能必须遵循特定的安全指南才能上市。在这种情况下,您可以根据安全准则和阈值为您的模型准确性建立目标。
**示例:**医疗保健等行业的各种产品。需要通过管理机构制定的指导方针,才能被认为可以安全使用。您可以利用这些特定的指南来估计您的模型的目标精度阈值。这些因行业、产品类型、地区等因素而异。
通过使用上述方法之一,您可以建立一个为您的模型设置目标的系统方法,而不是听起来不错但实际上无法实现或与您的产品目标不一致的任意目标。
如何发展成为一名数据科学家
本文向您展示了如何提高您的数据科学技能。特别是,这可能适用于希望转换角色的数据科学家或进入该领域的其他人。请将此视为一个灵活的框架,以指导您在数据科学职业生涯中的下一步。这是我通常使用的方法,我认为它在你想要的和保持真实(市场想要的)之间取得了很好的平衡。首先,我们将讨论您想对数据科学做些什么。第二,我们会了解市场的需求。第三,我们将讨论软技能——与人打交道。最后,我们将结束我对就业市场竞争的小演讲。我们开始吧!
详细说明
数据科学有很多应用方式。获得优势的一个方法是将您的数据科学技能集专门化于特定的专业领域。找到你的目标行业是一个好的开始,因为它不会把你束缚得太深。例如,我自己的专业领域是金融数据科学,也称为定量分析师。另一方面,我有几个同事在医疗保健领域应用了他们的数据科学技能。
在每个专业子集中,你可以自己创造有利可图的副业,并在简历中展示。在金融领域,这方面的例子包括信用评级或欺诈检测。当然,对于金融来说,还有一个试图预测股票价格的著名例子。在医疗保健领域,每年都会出现一些公开的竞赛。例如,我记得我的一个熟人告诉我一些健康数据科学竞赛的奖金超过 50 万美元+!
需求
一旦您有了想要进入的领域,您就应该查看该领域的一些数据科学家职位发布。具体来说,我们会看市场需求。每个工作和领域都有特定的要求或工作要求,这些要求或要求往往是相似的。例如,在金融领域,你可能需要 NLP(自然语言处理)方面的专业知识来理解任何给定资产的市场情绪,或者,在医疗保健领域,你可能需要预测特定蛋白质如何折叠。但作为浏览招聘信息的一般基准,你至少应该知道 SQL 和 Python 或 r。对于应用大数据的公司,你需要学习 Hadoop 和 Spark。
影响
在发展您的数据科学职业中获得优势的另一种方法是建立关系网。看看招聘经理那边,他们有数百名合格的申请人。他们的工作就是筛选出他们认为适合这个角色的人。你不觉得如果你亲自去见了招聘经理,并且能够满足那个职位的需求,你会更有可能得到面试机会吗?你当然会!此外,用你的专业技能给人留下一个好印象也会在以后有所收获。例如,你现在遇到的一个人可能现在没有在招人,但是以后他们可能需要你的技能,或者知道谁需要。
人际关系网的另一个好处是,在你出名的过程中,你会遇到其他有趣的人,这些人会激励你去做一些有趣的项目。在这些有趣的项目中,你永远不知道哪一个会变成潜在的合作,比如创业或自由职业。例如,我最近去了一家初创公司的聚会,只是为了四处看看,并得到了兼职机会。
竞争
最终,任何工作申请都只允许一个职位空缺。当然,也有一些例外(例如,公司组建团队),但假设只有一个位置更安全。记住,在申请职位时,赢家通吃。换句话说,第二名或以下没有奖励。在这种情况下,你最好的策略是,用卡尔·纽波特的话说,“好到他们不能忽视你。”
但是不要让表面上的坏运气阻止你实现目标。我们的生活中都有消极的人。有些人可能会说你没有这个,不能做那个,等等。呆的时间足够长,一些消极情绪就会开始影响到你。放手去做,超越竞争对手。
没有一个 X 技能(用任何技能代替 X)?出去想办法。每天采取任何行动。这是你发展优势的地方,这将转化为你可以推动更大进步的竞争优势。例如,我有一些我从未想过要效仿的榜样。我只是每天研究和实践我的发现。在几个月的时间里,我最终获得了这些技能,并提高了那些伴随我而来的技能。这里有一个你们可能不知道的小提示:
没有人在 100%的时间里发展自己。总有一天,学习曲线会变平,他们会去度假,生活会发生变化,等等。这给你留下了上升的机会。
关键是到达目标的攀登总是可以到达的。你只需要愿意足智多谋,并付出努力去实现它。
结论
在任何领域,尤其是科技领域,由于日新月异的变化,优秀的人会不断地学习和提高自己。作为一名数据科学家,你的目标是通过数据驱动的洞察力解决其他人无法解决的问题。有了 idea,你必须具备计算机科学、统计学和领域的专业知识才能玩这个游戏。要被录用,你必须是最优秀的。当然,一个人不可能无所不知,这就是为什么公司会为专家支付额外费用。一旦你选择了你的行业,学习该领域流行的技术和技能,并解决该领域的相关问题,以增加你的项目组合。数据科学领域的招聘经理真正强调的是你解决问题的能力,以及理解你为什么会有你的解决方案。所以,作为一个实用的提示,记得解释你的每个项目是如何解决一个问题的,你做了什么,为什么这么做,以及结果。
发展技能和创造市场回报的东西需要时间。一路上有许多挫折。但是请记住:
如果你想要你从未拥有过的东西,你必须愿意去做你从未做过的事情。——托马斯·杰斐逊
免责声明:本文陈述的所有内容均为我个人观点,不代表任何雇主。
如何开发数据产品而不死尝试
作者:
大卫·弗洛雷斯·费尔南德斯 、数据与 AI 解决方案架构师@微软 巴勃罗·帕丽丝 、数字架构师@微软
公司努力通过分析项目发展壮大
在当今数据积累的时代,全球渴望人工智能在各个层面的创新和商业应用。也许是时候停下来反思一下那种到处使用 AI 的燃烧欲望,并考虑一下“乐器定律”:“如果你只有一把锤子,那么一切看起来都像钉子”。
TL;DR:通过最后 10 个问题来评估你在构建数据产品方面有多擅长。
有许多报告警告公司在业务中实施或采用人工智能时遇到的问题。Gartner 指出 60%的分析和 BI 项目失败了,后来认识到实际情况更糟。根据 Gartner 分析师 Nick Heudecker 的说法,Gartner 对 60%的估计“过于保守”。真正的失败率接近 85%。".因此,如果你正面临任何困难,请认为你并不孤独,如果你不是,请认为你可能错过了一次现实检查。根据我们与不同行业和国家的客户打交道的经验,公司最常遇到的情况可以总结如下:听起来耳熟吗?
1.**没有预算经验:**投资大,兴趣大,但没有开发高级数据和人工智能项目的经验,这意味着在取得任何结果之前都会浪费大量时间。
2.不支持:没有业务支持的公司。许多好的想法由于缺乏合适的利益相关者的暗示而被搁置。
3.目标明确但效率低下的团队发展:团队努力工作却一事无成,或者结果令公司失望。
4.雄心勃勃的公司:有预算,有时有无限的资源,但不担心技术限制。当试图将解决方案应用于大规模问题时,这种情况可能会导致挫折。
5.**缺少数据:**数据不完整或完全孤立。这些类型的公司需要首先投资建立和准备他们的数据系统,以便能够进入高级分析。我们还可以在这里包括数据质量的问题。这不是一个小问题。
6.理论上的:公司拥有优秀的分析团队,但不了解敏捷方法或数据科学,难以扩大规模或将模型投入生产。
7.天真:相信 ML 有魔力的公司。祝你好运!
我们如何避免主要的陷阱?
本文的目的是作为讨论的种子,并提出一种不同的方法来执行和理解高级分析在企业架构中的正确位置。
想想看,很多时候这些项目的完成方式基本上是使用一个目标很少与公司的优先事项一致的供应商。我们可以在多大程度上外包数据产品的开发?供应商试图开发最准确或最适合的模型,而不关注工作的潜在和最终目的,这种情况并不罕见,当然,这是有用的。他们有没有搞清楚准确率和 ROI 之间的关系?相关吗?部署了一个非常好的模型,但没有人能够将其转化为金钱、新收入或成本节约,这种情况并不少见。那么,我们是否部署了一个非常好的模型,但却毫无用处?或者相反,我们是否部署了一个非常糟糕的模型,它并不比特定数据集的随机模型好多少?一旦模型被部署,谁来照看它们?问题太多…
总结一下,这些是公司必须集中精力的基本部分:使用和适用性。一家公司计划使用数据和人工智能构建的任何东西都必须遵循开发产品的理念。公司必须像为客户创造新产品一样痴迷于他们的开发过程。要么让它盈利,要么抛弃它。
此外,一个为了 PoC 而参与 PoC 的公司,仅仅关注技术组件往往是一种不完整的努力,也可能导致失败,不仅因为模型或数据不好,还因为失去了业务方向。这就是为什么我们在这篇文章中提出的不是孤立的工作,而是以敏捷的方式开发一个产品的完整版本,虽然功能有限,但是需要所有利益相关者的参与。软件世界中没有什么新东西,但显然还没有深入到那些创造数据驱动解决方案或产品的人的头脑中。
为了构建一个数据产品,你需要什么?
希望在这个阶段,经验告诉我们的事情已经很清楚了:团队、方法和责任的清晰定义不应该被低估。这就是我们提出数据产品开发隔离策略的原因。在我们的下一篇文章中,我们将对此进行更深入的探讨。例如,每个工作流必须如何独立工作,以及所有团队如何在不造成混乱的情况下协作。同时,这是我们对团队、重点领域以及成员的建议:
Main team members and focus areas
同时,关于哪种方法更好还有很多可以讨论的,但是我们建议不要在选择完美的方法上浪费太多时间。应用的交付方法可以是你公司规定的任何方法:敏捷、团队数据科学过程、设计架构会议、设计思维、UX 设计,这里仅举几个例子。重要的一点是选择一种与您团队的技能相一致的方法,并关注最终的业务成果。
为了说明不同团队之间关系的复杂性,请参见下图,其中我们描述了实现完整 PoC 或功能发布的主要里程碑和依赖关系。
Workstrems involved in Data Product development
结论
我们只是触及了可能阻碍或减缓您的数据项目的表面原因,我们承认许多公司正在努力使他们的数据项目盈利或仅仅是有用,我们已经提出了一种组织不同工作流的方法。下一步是什么?
为什么不开始思考之前提到的五个主题以及你的公司是如何解决它们的:
1.方法学
2.数据
3.组
4.技术
5.商业导向
并向您的团队提出以下问题:
1.你的团队有能力在短时间内展示切实的成果吗?
2.这些结果与您的团队最初试图解决的业务问题直接相关吗?
3.与问题直接相关的业务人员能够建议参数配置吗?
4.技术在某种程度上影响了你的决定吗?例如,“我的团队觉得使用 SAS 很舒服,我们没有大数据,所以除此之外我们不需要任何其他技术。”
5.你的团队意识到你的模型的准确性和货币影响之间的关系了吗?例如,您的模型中 1%的准确性对您的业务目标有任何意义吗?
6.你能使用 3 个月前你尝试过的完全相同的数据集重新创建你的实验并比较结果吗?
7.你的团队使用任何源代码控制工具吗?有测试工具吗?除了笔记本之外还有其他工具吗?有人为你的团队写的代码做代码评审吗?
8.你能解释一下你的算法的结果吗,以防在现实世界中出错?例如,在给出信用的假阳性的情况下,你能向风险部门解释为什么吗?
9.你有将模型引入生产的流程吗?您能执行负载测试吗?性能测试?不停止服务就部署?
10.你的公司里有道德顾问的角色吗?法律部门了解您的数据产品吗?
如果你对这篇文章的内容感兴趣,请花几分钟时间回答这个表格,让我们知道你如何看待自己在开发数据产品。这是绝对匿名和保密的。
参考资料:
https://hbr.org/2018/10/how-to-build-great-data-products
如何用人工智能发现 Spotify 上的新音乐
Photo by author
寻找你可能从未听说过的艺术家的类似音乐
更新:自己试试这里,这里
[## deej-a . I .-Google Play 上的应用程序
使用人工智能发现新音乐,根据音乐的声音自动生成播放列表…
play.google.com](https://play.google.com/store/apps/details?id=online.deejai.www)
或者这里:
https://apps.apple.com/us/app/deej-a-i/id1529860910?mt=8
作为一名 DJ,我最讨厌的问题是“你有什么[在此插入完全不合适的艺术家]?”“你听哪种音乐?”。在我看来,一旦你把音乐放进一个流派的盒子里,它就会被它的限制所束缚。但是,如果不贴上标签,如何分享和组织音乐呢?
Spotify 能够根据特定曲目创建播放列表(或“电台”)。它非常擅长选择相似艺术家的音乐,但是不可避免地,它倾向于播放你可能已经熟悉的音乐。也许这就是你想要的,但是如果你想要一些你从未听过的新音乐的推荐呢?在 MP3 发明之前的美好时光里,我会去伦敦皮卡迪利大街的果酱唱片店。奥利会知道我到底在找什么样的东西,而我会在听了几秒钟后就立刻知道我是否喜欢一首曲子……这么说吧,一大笔钱转手了。
毫无疑问,有大量的音乐是我不知道的,但是没有像奥利这样的人来帮助我,我怎么能找到它呢?我不想把音乐按类别分类,而是想出一种连续的方式来组织和探索它。我不是通过关于艺术家或歌曲的信息来寻找,而是通过歌曲听起来的方式来寻找相关的东西。如果你对我如何编写一个名为 Deej-A.I .的程序的技术细节感兴趣,在不使用任何标签或任何未公开可用的数据的情况下做到这一点,那么看看这篇文章。
我在所有 Spotify 曲目上运行了我的程序,这些曲目(a)在播放列表中至少出现 10 次(截至 2018 年 10 月),并且(b)可以以 30 秒 MP3 样本的形式下载。这给了我两种关联 Spotify 歌曲的方法:一种是根据它们在播放列表中出现的接近程度,另一种是根据它们听起来的相似程度。第一种方式倾向于将相似艺术家的歌曲分组,而第二种方式将具有相似能量、情绪、节奏或乐器的歌曲放在一起。从技术上讲,这些被称为多维空间中的嵌入,其中一个轨道与另一个轨道的接近程度表示它们彼此有多相似。有趣的是:我们可以混合这两种关联音轨的方式,从而控制我们对发现类似音乐的开放程度,这些音乐可能来自我们从未听说过的艺术家。我将把这个混音参数称为创意,其中 0 表示由相似的艺术家创作,1 表示由相似的声音创作。
例如,如果我们让 Deej-A.I .根据 Luis Fonsi 的“ Despacito ”的声音生成一个播放列表(我应该说这是而不是我听的那种音乐:-),那么我们会得到以下结果。
有趣的是,我们最终得到了一堆听起来非常相似的歌曲(自动调音的人声,毁灭的节奏等等),但它们来自世界各地,使用各种不同的语言。也许这是你的事,我没资格评判。
随着新航的"吊灯"它挑选出一些惊人相似的声音歌曲,包括新航自己的一首。请记住,当创造力设置为 100%时,除了音乐听起来如何之外,没有什么可以继续。
如果我们给它一些更有挑战性的东西呢?顾名思义,“比蚊子的高音喇叭还恐怖”并不是妮娜·西蒙的典型特征。以下是 Spotify 给出的结果。
另一方面,Deej-A.I .因为它实际上是在听音乐,掌握时髦的非洲节奏,并提供更加连贯的播放列表。在这种情况下,我选择了 50%的创造力,以便获得类似艺术家的类似声音推荐。
将点连接起来
生成播放列表最明显的方法是添加与刚刚播放过的歌曲最接近的歌曲。正如你所看到的,这可以产生一个有趣但曲折的路径通过嵌入空间。如果你走得够久,你永远无法确定你会在哪里结束。
另一种方法是我称之为“连点”(这个术语是我从 DJ 吉勒斯·彼得森那里借来的)。您选择第一首和最后一首曲目,以及沿途您想要访问的任何其他曲目,播放列表将从最接近沿连接这些曲目的线等间距点的曲目中生成。换句话说,我们在尽可能接近的选定轨迹之间进行线性插值。这可能会导致一些有趣的结果,因为嵌入空间中的不同方向可能会以某种方式编码音乐的某些方面,如能量、人声的存在和速度等。当然,如果你计划举办一个聚会,你可以用它来开始一些放松的东西,然后通过一些众所周知的最爱,最后以室内音乐结束…
如果我们要求它制作一个播放列表,从*的《权力的游戏》的主题音乐开始,到的《维京人》*的主题音乐结束,我们看看它会得到什么。
如果我们尝试一些不同的流派呢?这是我之前做的一个播放列表,从迪斯科屋到电子乐,有 50%的创意。
如果你有灵感(并且有一个谷歌账户),你可以自己试一试。只需点击下面的链接,在“沙盒模式”下打开,连接到一台机器,然后按下播放按钮。
编辑描述
colab.research.google.com](https://colab.research.google.com/github/teticio/Deej-A.I./blob/master/Spotify_Discovery.ipynb)
如果你想出了一个有趣的播放列表,请在评论中加入链接。快乐聆听!
如何在 BigQuery 中直接批量预测张量流模型
BigQuery ML 现在支持 TensorFlow SavedModel
如果您已经在 TensorFlow 中训练了一个模型,并将其导出为 SavedModel,那么您现在可以使用 ML。BigQuery 中的 PREDICT SQL 函数进行预测。如果您想要进行批量预测(例如,对过去一小时收集的所有数据进行预测),这非常有用,因为任何 SQL 查询都可以在 BigQuery 中调度。
步骤:
- 在 TensorFlow 中训练和导出保存的模型
- 在 BigQuery 中,创建一个模型,传递保存的模型的位置
- 用 ML。评估,ML。预测等。就像在 BigQuery 中使用其他(内置)模型类型训练模型一样。
注意:该功能目前处于公开测试阶段。联系您的 GCP 代表获得白名单。
1.在 TensorFlow/Keras 中训练和导出保存的模型
我将使用我在这篇博文中描述的文本分类模型进行演示,其 Keras 代码在 GitHub 上。我在 Cloud ML 引擎上训练模型,但你可以在任何地方以任何方式训练它。重要的一点是将模型作为保存的模型导出到 Google 云存储中:
exporter = tf.estimator.LatestExporter('exporter', serving_input_fn)
2.创建模型
在 BigQuery 中创建模型只需指定一个不同的 model_type,并将其指向导出 SavedModel 的 model_path(注意末尾的通配符,用于选择资产、词汇等)。):
CREATE OR REPLACE MODEL advdata.txtclass_tf
OPTIONS (**model_type='tensorflow',**
**model_path=**'gs://cloud-training-demos/txtclass/export/exporter/1549825580/*')
我已经将上面的 bucket 设置为 public,所以您可以按原样尝试上面的查询(首先创建一个名为 advdata 的数据集)。这将在 BigQuery 中创建一个模型,其工作方式类似于任何内置模型:
特别是,该模式指出模型所需的输入称为“输入”,并且是一个字符串。
3.用模型预测
用模型预测非常简单。例如,我们可以从 BigQuery 表中提取一些数据,并确保在 select 中,我们根据 TensorFlow 的要求来命名列。在这种情况下,我的 TensorFlow 模型的 serving_input_fn 指定该模型需要一个名为“input”的输入字符串。
鉴于此,我们现在可以做一个预测:
WITH extracted AS (
SELECT source, REGEXP_REPLACE(LOWER(REGEXP_REPLACE(title, '[^a-zA-Z0-9 $.-]', ' ')), " ", " ") AS title FROM
(SELECT
ARRAY_REVERSE(SPLIT(REGEXP_EXTRACT(url, '.*://(.[^/]+)/'), '.'))[OFFSET(1)] AS source,
title
FROM
`bigquery-public-data.hacker_news.stories`
WHERE
REGEXP_CONTAINS(REGEXP_EXTRACT(url, '.*://(.[^/]+)/'), '.com$')
AND LENGTH(title) > 10
)
)
, input_data AS (
SELECT title AS input FROM extracted limit 5
)SELECT *
FROM ML.PREDICT(MODEL advdata.txtclass_tf,
(SELECT * FROM input_data))
这提供了结果:
知道了实际的标签,我们可以使实际的查询更好:
SELECT
input,
(SELECT AS STRUCT(p, ['github', 'nytimes', 'techcrunch'][ORDINAL(s)]) prediction FROM
(SELECT p, ROW_NUMBER() OVER() AS s FROM
(SELECT * FROM UNNEST(dense_1) AS p))
ORDER BY p DESC LIMIT 1).*FROM ML.PREDICT(MODEL advdata.txtclass_tf,
(
SELECT 'Unlikely Partnership in House Gives Lawmakers Hope for Border Deal' AS input
UNION ALL SELECT "Fitbit\'s newest fitness tracker is just for employees and health insurance members"
UNION ALL SELECT "Show HN: Hello, a CLI tool for managing social media"
))
请注意,这个函数直接在 SQL 中提供值。结果是:
就是这样!
如何在黑盒模型上进行贝叶斯超参数调整
云 ML 引擎上任意函数的优化
谷歌云 ML 引擎提供了一个使用贝叶斯方法的超参数调整服务。它不限于 TensorFlow 或 scikit-learn。事实上,它甚至不仅限于机器学习。您可以使用贝叶斯方法来调整几乎任何黑盒模型。
为了进行演示,我将调整一个流量模型,以找到能使给定流的延迟最小的流量配置。
I’ll demonstrate the hyper-parameter tuning on a traffic model. Photo by Denys Nevozhai on Unsplash
1.调用要优化的黑盒函数
为了简单起见,我将在 Python 本身中实现“黑盒函数”(它改编自几个数值软件包中使用的一个例子)。在现实生活中,您可以调用任何可执行文件,因此您不局限于 Python。只要确保得到一个与您想要最小化或最大化的东西(在这个例子中是延迟)相对应的浮点值。
def compute_delay(flow, x12, x32):
# traffic on other roads, assuming all traffic that arrives
# at an intersection has to leave it
x13 = flow - x12
x24 = x12 + x32
x34 = x13 - x32 # travel time on each road segment
t12 = 5 + .1 * x12 / (1\. - x12 / 10.);
t13 = x13 / (1\. - x13 / 30.);
t32 = 1\. + x32 / (1\. - x32 / 10.);
t24 = x24 / (1\. - x24 / 30.);
t34 = 5 + .1 * x34 / (1\. - x34 / 10.);
# total delay
f = t12*x12 + t13*x13 + t32*x32 + t24*x24 + t34*x34;
return(f);
给定节点间的交通流量和我们期望的车辆/小时总流量,上面的模型计算道路网络中的总交通延迟。
我们需要为某个流量值找到 x12 和 x32 的最佳值。
2.创建命令行参数
确保每个可优化的参数都是命令行参数:
parser = argparse.ArgumentParser()
parser.add_argument('--x12', type=float, required=True)
**parser.add_argument('--x32', type=float, required=True)**
3.调用 hypertune 软件包
然后,写出我们希望使用 hypertune 软件包优化的指标:
hpt = hypertune.HyperTune()
**hpt.report_hyperparameter_tuning_metric**(
**hyperparameter_metric_tag='delay'**,
metric_value=delay,
global_step=1)
4.编写 hyperparam.yaml 文件
编写一个配置文件。该文件应该包含要在其上运行代码的机器的类型、要优化的指标的名称以及要优化的每个参数的约束条件:
trainingInput:
scaleTier: CUSTOM
**masterType: standard # machine-type**
hyperparameters:
goal: MINIMIZE
maxTrials: 10
maxParallelTrials: 2
**hyperparameterMetricTag: delay**
params:
- parameterName: x12
type: DOUBLE
minValue: 0
maxValue: 10
scaleType: UNIT_LINEAR_SCALE
**- parameterName: x32
type: DOUBLE
minValue: 0
maxValue: 10
scaleType: UNIT_LINEAR_SCALE**
5.将超参数调整作业提交给 ML 引擎
您可以使用 REST API 或 Python,但最简单的方法是从命令行使用 gcloud:
#!/bin/bash
BUCKET=<yourbuckethere>
JOBNAME=hparam_$(date -u +%y%m%d_%H%M%S)
REGION=us-central1
gcloud ml-engine jobs **submit training** $JOBNAME \
--region=$REGION \
--module-name=**trainer.flow** \
--package-path=$(pwd)/trainer \
--job-dir=gs://$BUCKET/hparam/ \
**--config=hyperparam.yaml** \
-- \
--flow=5
6.在 GCP 控制台上查看结果
等待作业完成,并在 GCP 控制台上查看结果。我有:
{
"trialId": "8",
"hyperparameters": {
**"x12": "0.70135653339854276",
"x32": "0.018214831148084532"**
},
"finalMetric": {
"trainingStep": "1",
**"objectiveValue": 50.2831830645**
}
},
这种道路配置的实际最佳值是 40.3 的延迟;即使搜索空间很大并且问题是非线性的,我们在 10 次尝试中还是得到了相对接近的结果。
后续步骤:
- 试试看。的完整代码在 GitHub 上。
- 阅读关于超参数调整的 ML 引擎文档。
- 阅读可用的机器类型的 ML 引擎文件。
如何使用 DataOps 提高数据质量
利用数据测试和安全环境使数据质量成为接触数据的每个人的日常活动
Image Source: Pixbay
坏数据的代价
数据质量差的成本如此之高,以至于许多人难以相信这些统计数据。Gartner 估计,由于数据质量差,平均每个组织每年都会损失1500 万美元。对于一些组织来说,这甚至是致命的。我经常想起我的数据科学创新峰会共同主持人、来自多米诺数据实验室的丹·恩托文讲的一个故事,关于一家高频交易公司骑士资本,他们在没有测试其效果的情况下部署了一个错误的算法更新。一天之内,该公司就自动取走了几乎所有的资本,并不得不策划向另一家公司的紧急出售。
他还提到一家信用卡公司未能验证来自第三方提供商的 FICO 信用评分字段。当该公司后来更换提供商时,新的提供商显示“无信用”,非法值为 999 (850 是最高合法值)。因为没有适当的数据质量检查,他们的自动批准算法开始批准这些具有巨大信贷限额的申请,导致重大损失。
DataOps 确保保持数据质量
在之前的一篇文章中,我讨论了向数据操作的深刻转变,这将改变数据管理的方式。就在最近,我们获得了大规模重复版本和复制数据的能力,这将使我们能够从应用于数据系统的流程和控制(即控制委员会会议)转向系统内的保证。将受到影响的数据治理的关键要素之一是数据质量管理,它将从分配给试图在数据被破坏后“清理”数据的数据管理员的任务转移到产生数据的组织内所有开发活动的一部分。
首先是几个定义,这样我们就在同一页上了:
在我们开始之前,最好给出一些将在本文中使用的定义(因为我们正在讨论数据质量,所以最好是精确的):
数据源 —随着时间的推移生成数据并使该数据作为数据实体可供消费的任何东西。可能是运营应用程序、精细数据仓库、第三方 API、传感器等。
数据产品 —满足特定需求的数据实体,通常通过转换和组合来自数据源的数据而产生
数据管道 —一系列的转换,在这些转换中,数据从数据源流入,从数据源流出,进入数据产品
数据定义 —描述特定数据实体的元数据。该定义应该使数据消费者能够在没有额外输入的情况下使用数据实体。它通常包括实体的位置、模式和格式,以及每个字段的描述和约束。
数据质量是每个人工作的一部分
同步的另一个重要定义是您组织中使用数据产生见解的广泛人群。我称他们为数据产品创造者。他们可能来自各种背景,通常拥有数据分析师、数据工程师或数据科学家的头衔。每一个都将遵循稍微不同的开发过程,并且可能使用不同的工具集。然而,他们都应该有责任维护和提高数据质量。
The 3 types of Data Product Creators all have data quality responsibilities
为了将数据质量从事后数据管家操作转移到日常生活中,所有接触数据的角色都必须参与到保持测试最新的过程中,以保持数据的整洁。这意味着所有创建数据产品的角色都必须考虑通常分配给数据管理员的任务。这并不意味着数据管理员的角色应该完全消失,因为组织中仍然有大量的数据源和数据集没有进行积极的开发。
数据操作和测试驱动的开发
编译器需要比它们编译的代码更健壮。鉴于数据管道代码实际上只是将数据编译成数据产品,并且在编写代码时数据本身不受您的控制,您的数据管道代码应该足够健壮以处理任何可能的数据场景。当构建库代码时,软件开发人员通常利用像微软 Intellitest 这样的工具来生成考虑到所有可能的输入场景的测试输入。
“我们希望管道所有者将模式视为与源代码同等的生产资产,并采用最佳实践来审查、版本化和维护模式。”—谷歌数据工程
在 DataOps 中,当开发使用数据作为输入的新代码(即数据管道代码)时,数据定义(即可能值的模式和规则集)必须准确表示输入数据的预期约束。为了引导这个过程,像 Great Expectations 这样的库可以用来从示例数据中生成数据定义的约束。从那里,数据工程师或分析师可以修改和添加额外的约束。接下来,一个数据模糊库,比如谷歌的 LibFuzz 结构化模糊库或 Faker ,可以用来基于数据定义中的约束生成潜在的数据输入。基于这些测试产生的失败,输入有助于强化处理代码和数据定义。关于谷歌使用的实践的更多细节可以在本文中找到。大多数情况下,这种能力揭示了使数据定义更清楚地了解什么是可能的所需的更新,但有时它也揭示了可能破坏数据管道的代码错误。
数据测试和代码测试有许多共同的特点。这些测试被提交到源代码控制中,并且可以作为持续集成过程的一部分运行,使用像 Jenkins 或 Circle CI 这样的工具,测试的覆盖率可以根据样本数据集进行测量。然而,数据测试在一个重要方面有所不同:如果它们失败了,就会留下坏数据需要清理。在传统的数据开发架构中,确保数据管道真正工作的唯一方法。因此,在创建测试以评估数据质量时,数据产品创建者应该在安全的开发环境中工作,使用 DataKitchen 所谓的修复架构的权利。这允许他们创建或修改数据产品,而不会将坏数据引入生产中。
在许多情况下,测试变得更加困难,因为交付的数据产品只是生成它们的数据输入的表示(例如填充仪表板的图表和图形)。在这种情况下,很难编写基于代码的单元测试来为数据集指定完整的约束集。因此,这组测试通常确保管道不会以意外的方式中断,但不能保证数据是高质量的。这与编译器不检查代码中的业务逻辑缺陷非常相似。在敏捷软件开发中,编码人员必须与产品负责人一起工作,为要接受的特性定义测试。在数据产品开发中,数据所有者应该能够指定他们在数据中期望的条件,而不需要太复杂的编码。
数据定义和持续集成
这样,数据质量的定义就成为数据定义的内在内容:
- 数据定义被视为一级代码:版本化、测试和发布
- 在合并或混合多个输入的流水线步骤中,输入被指定为一个组,允许为输入数据之间的关系编写测试。
- 当数据加载到生产中的数据平台时,会根据数据定义对数据进行检查。这些检查可用于将统计过程控制应用于运行平台。
通过结合这些原则,团队可以保证在接收时捕获数据问题。
The data validation procedure in use at Google, from: Data validation for machine learning
调试问题和根本原因自动化
在任何系统中,最终用户都会发现一些问题。在自助服务的世界里,我们希望数据消费者能够快速、轻松地报告他们正在利用的数据产品的问题。仅仅能够报告问题,并为其他数据消费者记录这些问题并不能建立信任。对于数据所有者来说,快速确定根本原因并解决问题非常重要。然而,众所周知,诊断数据问题既困难又耗时。出现的问题是:
- What :问题是由代码中的错误还是来源处的错误输入数据引起的?
- 哪里:如果是一个代码错误,这个问题是在管道的哪个阶段引入的?
- 如何:如果是源问题,我们对源数据的理解是否正确?
- 谁:如果是源问题,用户是否贡献了其他不良数据?
- 多大:还有哪些数据产品使用同样的不良输入数据(因此也是不正确的)?
- When :在哪个时间点引入了数据问题(存在多少其他类似的坏数据)?
有了数据和管道代码版本控制,以及针对管道中的数据持续运行的测试,我们可以更快地回答这些问题。
有了 DataOps 基础,就可以系统地获得上述根本原因(什么、哪里和如何)问题的答案。详细的数据沿袭允许我们利用以下过程:
- 与数据消费者合作,了解他们的期望
- 更新数据产品定义以包含这些期望
- 产生一个违背期望的数据测试(潜在地利用模糊测试工具来帮助这个过程)
- 确定导致测试失败的特定数据输入,并确定它们是否保证数据定义更新,因为它们是不现实的
- 如果数据输入是真实的,返回到管道中的前一阶段,并为新的输出预期添加测试
- 继续向后移动并添加测试,直到您发现输出中的问题与一组特定的输入无关。在这种情况下,有问题的管道阶段是问题的根本原因。
- 如果到达了管道的数据源,那么源数据就是问题所在,您需要适当地丢弃管道中的数据。
由此,可以使用数据分析来确定数据问题是何时出现的。源数据上的元数据可用于确定谁导致了问题,而谱系图可以通过从数据问题的来源到其他受影响的数据产品来回答问题有多严重。
数据问题文档和解决方案
数据谱系图还为我们提供了通知受检测到的数据问题影响的数据产品的所有消费者的能力。考虑下面的高等教育注册场景,其中有多个数据管道来利用注册指标来帮助进行预算和营销。基于各种中间数据产品之间的谱系图,一旦在管道中检测到问题,系统就可以自动警告下游消费者。类似的警报模式可用于多种类型的问题(该图显示了作业失败和运行缓慢的作业中的 3 个示例问题)。这些警报允许数据消费者确定他们是否应该使用受影响的数据产品(仪表板、应用程序等)。),或者等待修复。
此外,谱系图可用于在系统中传播数据质量文档。当检测到上面显示的任何数据质量问题时,它可以自动添加为数据目录中数据定义的注释。然后,质量问题可以传播到所有下游数据产品,允许任何其他数据产品创建者了解他们正在使用的数据源中存在的数据质量问题。
这种质量问题的延续也会在没有应用校正代码的记录的元数据中记录下来。然后,当在数据管道内应用修复来纠正坏的源数据时,该修复可以传播到元数据,并且可以从源和受影响的数据定义中移除数据质量注释。
如何做 Java 的深度学习?
Image source (unsplash) via Praveen Thotagamuwa
介绍
不久前,我偶然发现了这款名为 Valohai 的生命周期管理工具(或云服务),它的用户界面以及简单的设计和布局给我留下了深刻的印象。我和 Valohai 的一名成员就当时的服务聊得很开心,并得到了一个演示。在此之前,我已经使用 GNU Parallel 、JavaScript、Python 和 Bash 编写了一个简单的管道——另外一个完全使用了 GNU Parallel 和 Bash。我也想过用现成的任务/工作流管理工具(如 Jenkins X、Jenkins Pipeline、Concourse 或 Airflow)替换移动部件,但由于各种原因,我没有继续这个想法。
回到我们最初的对话,我注意到许多关于 Valohai 的例子和文档都是基于 Python、R 以及各自的框架和库。缺乏基于 Java/JVM 的例子或文档。所以我借此机会做了些事情。
Valohai 鼓励我使用著名的 Java 库DL4J——Java 的深度学习来实现一些东西。
在了解了 Valohai 的设计、布局和工作流程后,我对它的初步体验已经给了我很好的印象。它是开发者友好的,并且制作者已经考虑了开发者和基础设施工作流程的各个方面。在我们的世界中,后者主要由开发人员或系统运行团队运行,我们知道其中的细微差别和棘手问题。你可以从网站的功能区找到更多关于它的功能。
**Achtung!**只是想让你知道,从这里开始,这篇文章将更具技术性,可能包含代码片段,并提到深度学习/机器学习和基础设施相关的术语。
我们需要什么,如何需要?
对于任何机器学习或深度学习项目或倡议,目前两个重要的组件(从高级别角度来看)是代码,它们将创建并服务于整个生命周期将在其中执行的模型和基础设施。
当然,在上述之前、期间和之后都需要一些步骤和组件,但是为了简单起见,我们假设需要代码和基础设施。
代码
对于代码,我选择了一个使用 DL4J 的修改示例,它是一个 MNist 项目,有 60,000 个图像的训练集和 10,000 个手写数字图像的测试集。这个数据集可以通过 DL4J 库获得(就像 Keras 提供了它们的库存一样)。在 DL4J Cheatsheet 中的 DatasetIterators 下查找 MnistDataSetIterator ,以获得关于该特定数据集的更多详细信息。
在开始之前看看我们将使用的源代码,主要的 Java 类叫做 org.deeplearning4j .前馈. mnist . mlpmnissinglerayerrunner。
基础设施
现在很明显,我们已经决定使用 Valohai 作为我们运行实验(模型的训练和评估)的基础设施来测试 Java 示例。 Valohai 识别 git 库并直接挂钩到它们,允许执行我们的代码,而不管平台或语言如何——我们将看到这是如何工作的。这也意味着,如果你是 GitOps 或基础设施即代码的坚定支持者,你会喜欢这个工作流。
为此,我们只需要一个在 Valohai 上的账户,我们可以利用一个自由层账户,当我们注册时,可以访问各种配置的几个实例。更多详细信息,请参见计划和定价下的自由层和对比图表。对于我们想做的,免费层是绰绰有余了。
Java 和 Valohai 的深度学习
正如我们所同意的,我们将使用这两种技术来实现我们的目标,即训练一个单层模型并对其进行评估,以及查看在 Valohai 上的端到端体验。
我们将把必要的构建和运行时依赖项捆绑到 Docker 映像中,并使用它来构建我们的 Java 应用程序,训练一个模型,并通过一个简单的 valohai.yaml 文件在 Valohai 平台上对其进行评估,该文件位于项目存储库的根文件夹中。
面向 Java 的深度学习:DL4J
简单的部分是,我们在这里不需要做太多,只需构建 jar 并将数据集下载到 Docker 容器中。我们有一个预构建的 Docker 映像,其中包含构建 Java 应用程序所需的所有依赖项。我们已经将此图像推送到 Docker Hub ,您可以通过搜索dl4j-mnist-single-layer找到它(我们将使用 YAML 文件中定义的特定标签)。我们已经选择使用 GraalVM 19.1.1 作为这个项目的 Java 构建和运行时,因此它被嵌入到 Docker 映像中(关于 Docker 映像的定义,请参见 Dockerfile )。要了解更多关于 GraalVM 的信息,请查看 graalvm.org 官方网站的资源。
编排
当从命令行调用 uber jar 时,我们进入MLPMnistSingleLayerRunner
类,该类根据传入的参数将我们导向预期的操作:
传递给 uber jar 的参数由该类接收,并由execute()
方法处理。
我们可以通过--action train
参数创建一个模型,并通过分别传递给 Java 应用程序(uber jar)的--action evaluate
参数评估创建的模型。
完成这项工作的 Java 应用程序的主要部分可以在下面提到的两个 Java 类中找到。
训练一个模型
可以通过以下方式从命令行调用:
这将在由执行开始时传入的--output-dir
指定的文件夹中创建名为mlpmnist-single-layer.pb
的模型(当成功时,在执行结束时)。从 Valohai 的角度来看,应该是放入我们做的${VH_OUTPUTS_DIR}中(见 valohai.yaml 文件)。
有关源代码,请参见类MLPMNistSingleLayerTrain.java。
评估模型
可以通过以下方式从命令行调用:
这期望一个名为mlpmnist-single-layer.pb
的模型(由训练步骤创建)出现在调用应用程序时由传入的--input-dir
指定的文件夹中。
有关源代码,请参见 MLPMNistSingleLayerEvaluate.java 类。
我希望这个简短的插图能够清楚地说明训练和评估模型的 Java 应用程序一般是如何工作的。
这就是我们所需要的,但是请随意使用其余的源(以及 README.md 和 bash 脚本)来满足您的好奇心和理解这是如何完成的!关于如何使用 DL4J 的更多资源已在帖子末尾的资源部分提供。
瓦罗海
作为一个平台,Valohai 允许我们松散地耦合我们的运行时环境、代码和数据集,正如你从下面 YAML 文件的结构中看到的。这样,不同的组件可以独立发展,而不会互相妨碍或依赖。因此,我们的 Docker 容器只包含构建和运行时组件。在执行时,我们在 Docker 容器中构建 uber jar,将其上传到一些内部或外部存储,然后通过另一个执行步骤从存储(或另一个位置)下载 uber jar 和数据集来运行训练。这样,两个执行步骤是分离的;例如,我们可以构建一次 jar,然后在同一个 jar 上运行数百个训练步骤。由于构建和运行时环境不应该经常改变,我们可以缓存它们,代码、数据集和模型源可以在执行时动态可用。
将我们的 Java 项目与 Valohai 基础设施集成的核心是定义执行项目文件夹根目录下 valohai.yaml 文件中的步骤的步骤。我们的 valohai.yaml 长这样:
Build-dl4j-mnist-单层-java-app 步骤说明
从 YAML 文件中,我们可以看到,我们首先使用 Docker 映像来定义这一步,然后运行构建脚本来构建 uber jar。我们的 docker 映像具有构建环境依赖项设置(即 GraalVM JDK、Maven 等)来构建 Java 应用程序。我们不指定任何输入或参数,因为这是构建步骤。一旦构建成功,我们希望将名为MLPMnist-1.0.0-bin.jar
(原始名称)的 uber jar 复制到/valohai/outputs
文件夹(由${VH_OUTPUTS_DIR}
表示)。该文件夹中的所有内容都会自动保存在项目的存储中,例如 AWS S3 存储桶。最后,我们定义要在 AWS 环境中运行的作业。
注意:*valo hai自由层不能从 Docker 容器内部进行网络访问(默认情况下这是禁用的),请联系支持人员启用此选项(我也必须这样做),否则我们将无法在构建期间下载 Maven 和其他依赖项。*
步骤运行-dl4j-mnist-单层列车-模型的说明
定义的语义与上一步类似,只是我们指定了两个输入,一个用于 uber jar ( MLPMnist-1.0.0.jar
),另一个用于 dataset(将被解压到${HOME}/.deeplearning4j
文件夹中)。我们将传递两个参数--action train
和--output-dir /valohai/outputs
。从该步骤创建的模型被收集到/valohai/outputs/model
文件夹中(由${VH_OUTPUTS_DIR}/model
表示)。
注意:在 Valohai Web UI 的 Executions 选项卡的输入字段中,除了使用 datum://或 http:// URLs 之外,我们还可以使用执行编号,即 *#1*
或 *#2*
来选择以前执行的输出。键入文件名的几个字母也有助于搜索整个列表。
步骤运行-dl4j-mnist-单层评估-模型的说明
同样,这一步类似于上一步,除了我们将传入两个参数--action evaluate
和--input-dir /valohai/inputs/model
。此外,我们再次指定了在 YAML 文件中定义的两个inputs:
部分,称为dl4j-java-app
和model
,它们都没有设置default
。这将允许我们使用 web 界面选择 uber jar 和我们希望评估的模型——它是通过步骤Run-dl4j-mnist-single-layer-train-model创建的。
希望这解释了上述定义文件中的步骤,但如果您需要进一步的帮助,请不要犹豫,看看文档和教程。
一旦我们有了一个帐户,我们就可以登录并继续创建一个名为mlpmnist-single-layer
的项目,将 git repohttps://github.com/valohai/mlpmnist-dl4j-example链接到该项目并保存该项目,快速浏览教程以了解如何使用 web 界面创建项目。
现在您可以执行一个步骤,看看结果如何!
构建 DL4J Java 应用程序步骤
转到 web 界面中的执行选项卡,使用*【创建执行】按钮复制一个现有的或创建一个新的执行,所有必要的默认选项将被填充,选择步骤Build-dl4j-mnist-single-layer-Java-app。***
对于环境*,我将选择 AWS eu-west-1 g2.2xlarge ,并单击页面底部的**【创建执行】*按钮,以查看执行开始。
训练模型步骤
进入 web 界面的执行页签,与上一步相同,选择步骤运行-dl4j-mnist-单层-列车-模型。您必须选择在上一步中构建的 Java 应用程序(只需在字段中键入 jar ),数据集已经通过 valohai.yaml 文件预先填充:
点击*【创建执行】*开始此步骤。
您将在日志控制台中看到模型摘要:
在执行期间和结束时,可以在执行主选项卡的输出子选项卡下找到创建的模型:
您可能已经注意到了输出子选项卡中的几个工件。这是因为我们在每个时期结束时都会保存一个检查点!在执行日志中注意这些:
检查点 zip 包含模型训练在该点的状态,保存在以下三个文件中:
训练模型>元数据
您可能已经注意到这些符号在执行日志中飞快地闪过:
这些符号触发 Valohai 拾取这些值(以 JSON 格式),用于绘制执行指标,在执行期间和执行之后,可以在执行主选项卡中的元数据子选项卡下看到这些值:
我们能够通过将一个监听器类(名为 ValohaiMetadataCreator )挂接到模型中来做到这一点,这样在训练期间,注意力会在每次迭代结束时传递给这个监听器类。在这个类的例子中,我们打印了历元计数、迭代计数和分数(损失函数值),下面是这个类的代码片段:
评估模型步骤
一旦通过前面的步骤成功地创建了模型,我们就可以开始评估它了。我们像前面一样创建一个新的执行,但是这次选择Run-dl4j-mnist-single-layer-evaluate-model步骤。在开始执行之前,我们需要再次选择 Java 应用程序( MLPMnist-1.0.0.jar )和创建的模型(MLPMnist-single-layer . Pb)(如下所示):
选择所需的模型作为输入后,点击*【创建执行】*按钮。这是一个比前一个更快的执行步骤,我们将看到以下输出:
模型分析后的评估指标和混淆矩阵将显示在控制台日志中。
我们可以看到,我们的培训活动已经产生了基于测试数据集的接近 97% 准确度的模型。混淆矩阵有助于指出一个数字被错误地预测为另一个数字的情况。也许这对模型的创建者和数据集的维护者来说是一个很好的反馈,可以做一些进一步的研究。**
问题依然存在(不在本文讨论范围之内)——当面对真实世界的数据时,模型有多好?
CLI 工具很容易安装和使用,参见命令行用法。
如果您还没有克隆 git 存储库,那么应该这样做:
*$ git clone [https://github.com/valohai/mlpmnist-dl4j-example](https://github.com/valohai/mlpmnist-dl4j-example)*
然后,我们需要将通过上一节中的 web 界面创建的 Valohai 项目链接到存储在本地机器上的项目(我们刚刚克隆的项目)。为此,请运行以下命令:
您将看到类似这样的内容:
选择 1(或适合您的选项),您应该会看到以下消息:
😁 Success! Linked /path/to/mlpmnist-dl4j-example to mlpmnist-single-layer.
使用 CLI 工具了解所有 CLI 选项的最快方法是:
*$ vh — help*
还有一件事,在继续之前,确保你的 Valohai 项目与最新的 git 项目同步,通过这样做:
*$ vh project fetch*
(在 web 界面的右上方,显示有两个指向彼此的箭头图标)。
现在,我们可以使用以下命令从 CLI 执行这些步骤:
*$ vh exec run Build-dl4j-mnist-single-layer-java-app*
一旦执行开始,我们可以通过以下方式检查和监控它:
我们还可以同时通过 web 界面看到上述更新。
关于如何使用 Valohai 的更多资源已在本文末尾的参考资料部分提供,还有一些关于如何使用 CLI 工具的博客文章,请参见【1】|【2】。
结论
如你所见,DL4J 和 T21 单独或组合使用都很容易上手。此外,我们可以在组成我们实验的不同组件上进行开发,即构建/运行时环境、代码和数据集,并以松散耦合的方式将它们集成到执行中。
本文中使用的模板示例是开始构建更复杂项目的好方法。您可以使用 web 界面或 CLI 通过 Valohai 完成您的工作!使用 CLI,您还可以将其与您的设置和脚本集成(甚至与 CRON 或 CI/CD 作业集成)。
此外,很明显,如果我在从事一个 AI/ML/DL 相关的项目,我就不需要关心创建和维护一个端到端的管道(过去许多其他人和我不得不这么做)——这要感谢 Valohai 的人们所做的出色工作。
感谢sky mind(背后的启动DL4J,用于创建、维护和保持免费)和valo hai让这个工具和云服务可以免费和商业使用。
请在下面的评论中留言或者发微博告诉我这是否有帮助,我也欢迎反馈。
资源
其他 DL4J 资源
损失函数
估价
瓦罗海资源
其他资源
最初发表于 https://blog.valohai.com。
如何用图卷积网络在图上做深度学习
第 2 部分:谱图卷积的半监督学习
图的机器学习是一项困难的任务,因为图的结构非常复杂,但也能提供丰富的信息。本文是关于如何使用图形卷积网络(GCNs)对图形进行深度学习的系列文章中的第二篇,GCNs 是一种强大的神经网络,旨在直接对图形进行处理并利用其结构信息。我将对上一篇文章做一个简要的回顾,但是你可以在这里找到这个系列的其他部分:
- 图卷积网络的高级介绍
- 谱图卷积的半监督学习(this)
在上一篇文章中,我对 GCNs 进行了高层次的介绍,并展示了如何根据相邻节点的表示来更新节点的表示。在这篇文章中,我们首先深入了解在上一篇文章中讨论的相当简单的图卷积中执行的聚合。然后我们继续讨论最近发布的图卷积传播规则,我展示了如何在小型社交网络 Zachary’s 空手道俱乐部的社区预测任务中实现和使用它进行半监督学习。如下所示,GCN 能够学习每个节点的潜在特征表示,该潜在特征表示将两个社区分成两个合理内聚和分离的聚类,尽管每个社区仅使用一个训练示例。
Latent node representations of Zachary’s Karate Club in a GCN at each training epoch.
简要回顾
在我上一篇关于 GCNs 的文章中,我们看到了一个简单的数学框架来表达 GCNs 中的传播。简而言之,给定一个 N × F⁰ 特征矩阵t5】x和一个图结构的矩阵表示,例如 G 的 N × N 邻接矩阵 A ,GCN 中的每一个隐层可以表示为hⁱ***= f(【t24) a)其中h⁰=x和 f 是一个传播规则。 每层 Hⁱ 对应一个n×f*ⁱ特征矩阵,其中每一行都是一个节点的特征表示。**
我们看到了以下形式的传播规则
- *f(hⁱ,a)=σ(ahⁱwⁱ),*和
- f(,a)=σ(d⁻âhⁱwⁱ**)凡
这些规则在通过应用权重*【wⁱ和激活函数 σ 进行变换之前,将节点的特征表示计算为其邻居 的特征表示的集合。我们可以通过将上面的传播规则 1 和 2 表示为f(hⁱ,a)= transform(aggregate(【a,hⁱ**),wⁱ)其中 )=【σ】(mwⁱ)和合计()=ahⁱ***
正如我们在上一篇文章中所讨论的,规则 1 中的聚合将结点表示为其相邻要素表示的总和,这有两个明显的缺点:
- 节点的聚集表示不包括它自己的特征,并且
- 度数较大的结点在其要素制图表达中将具有较大的值,而度数较小的结点将具有较小的值,这可能会导致梯度爆炸的问题,并使得使用对要素缩放敏感的随机梯度下降等算法进行训练变得更加困难。
为了解决这两个问题,规则 2 首先通过将单位矩阵添加到来实施自循环,并使用转换后的邻接矩阵= A****+I进行聚合。接下来,通过与逆度矩阵d⁻相乘来归一化特征表示,将集合变成平均值,其中集合的特征表示的比例对于节点度是不变的。**
在下文中,我将把规则 1 称为求和规则,把规则 2 称为平均规则。
光谱图卷积
Kipf 和 Welling 最近的一篇论文提出了使用光谱传播规则的快速近似光谱图卷积[1]:
与上一篇文章中讨论的求和规则和均值规则相比,谱规则的不同之处仅在于聚合函数的选择。虽然它有点类似于均值规则,因为它使用度数矩阵 D 的负幂来标准化聚集,但是标准化是不对称的。让我们试一试,看看它能做什么。
作为加权和的聚合
我们可以将我到目前为止介绍的聚合函数理解为加权和,其中每个聚合规则的不同之处仅在于它们对权重的选择。在继续讨论谱规则之前,我们首先来看看如何将相对简单的和与平均规则表达为加权和。
求和规则 为了了解如何使用求和规则计算第 i 个节点的集合特征表示,我们来看看如何计算集合中的第 i 行。
The Sum Rule as a Weighted Sum
如以上等式 1a 所示,我们可以将第 i 个节点的集合特征表示计算为向量矩阵乘积。我们可以将这个向量矩阵乘积公式化为一个简单的加权和,如等式 1b 所示,其中我们对 X 中的 N 行中的每一行求和。
等式 1b 中聚集的第 j 个节点的贡献由第 A 的第 i 行的第 j 列的值确定。由于是邻接矩阵,如果第 j 个节点是第 i 个节点的邻居,则该值为 1,否则为 0。因此,等式 1b 对应于对第 i 个节点的邻居的特征表示求和。这证实了前一篇文章中的非正式观察。
总之,每个邻居的贡献仅取决于由邻接矩阵定义的邻居。
平均值规则 为了查看平均值规则如何聚合节点表示,我们再次查看如何计算聚合中的第 i 行,现在使用平均值规则。为了简单起见,我们只考虑“原始”邻接矩阵上的平均规则,而不考虑和单位矩阵 I 之间的加法,这简单地对应于将自循环添加到图中。
The Mean Rule as a Weighted Sum
从上面的等式中可以看出,现在求导的时间稍微长了一点。在等式 2a 中,我们现在首先通过将邻接矩阵 A 乘以度矩阵 D 的逆矩阵来对其进行变换。这个计算在等式 2b 中变得更加清楚。逆度矩阵是一个对角矩阵,其中沿对角线的值是逆节点度 s.t .位置( i,i) 的值是第 i 个节点的逆度。因此,我们可以去掉求和符号之一,得到等式 2c。方程 2c 可以进一步简化,得到方程 2d 和 2e。
如等式 2e 所示,我们现在再次对邻接矩阵中的 N 行中的每一行求和。正如在讨论求和规则时提到的,这对应于对每个第 i 个节点的邻居求和。然而,等式 2e 中的加权和中的权重现在被保证用第 i 个节点的度求和为 1。因此,等式 2e 对应于第 i 个节点的邻居的特征表示的平均值。
求和规则仅依赖于由邻接矩阵定义的邻域,而平均值规则也依赖于节点度数。
光谱规则我们现在有了一个有用的框架来分析光谱规则。让我们看看它会把我们带到哪里!
The Spectral Rule as a Weighted Sum
与均值规则一样,我们使用度矩阵 d 来变换邻接矩阵 A。然而,如等式 3a 所示,我们将度矩阵提升到-0.5 的幂,并将其乘以 A 的每一侧。该操作可以分解为等式 3b 所示。再次回忆,度矩阵(及其幂)是对角。因此,我们可以进一步简化方程 3b,直到得到方程 3d 中的表达式。
等式 3e 显示了一些非常有趣的东西。当计算第 I 个节点的聚集特征表示时,我们不仅考虑第 I 个节点的度,还考虑第 j 个节点的度。
与平均值规则类似,光谱规则对聚合 s.t .进行归一化。聚合要素制图表达与输入要素保持大致相同的比例。但是,谱规则在加权和中对低度数邻居的权重较高,对高度数邻居的权重较低。当低度相邻比高度相邻提供更多有用信息时,这可能是有用的。
基于 GCNs 的半监督分类
除了谱规则,Kipf 和 Welling 还演示了 GCNs 如何用于半监督分类[1]。在半监督学习中,我们希望利用有标签和无标签的例子。到目前为止,我们已经隐含地假设整个图是可用的,也就是说,我们处于转导设置中。换句话说,我们知道所有的节点,但不知道所有的节点标签。
在我们看到的所有规则中,我们在结点邻域上进行聚合,因此共享相邻结点的结点往往具有相似的要素表示。如果图表现出同质性,即连接的节点倾向于相似(例如具有相同的标签),则该属性非常有用。同向性存在于许多现实网络中,尤其是社交网络表现出强烈的同向性。
正如我们在上一篇中看到的,即使是一个随机初始化的 GCN 仅仅通过使用图结构就可以实现同形图中节点的特征表示之间的良好分离。我们可以通过在标记的节点上训练 GCN 来更进一步,通过更新在所有节点上共享的权重矩阵来有效地将节点标记信息传播到未标记的节点。这可以按如下方式完成[1]:
- 通过 GCN 执行前向传播。
- 在 GCN 的最后一层按行应用 sigmoid 函数。
- 计算已知节点标签的交叉熵损失。
- 反向传播损失并更新每层中的权重矩阵 W。
扎卡里空手道俱乐部中的社区预测
让我们看看谱规则如何使用半监督学习将节点标签信息传播到未标签节点。正如在上一篇中一样,我们将以扎卡里的空手道俱乐部为例。
如果您想继续学习,您可以在 Jupyter 笔记本上找到数据集,其中包含训练和评估 GCN 的代码,请点击这里 。
扎卡里空手道俱乐部
简而言之, Zachary 的空手道俱乐部是一个小型社交网络,在这里,空手道俱乐部的管理员和教练之间发生了冲突。任务是预测空手道俱乐部的每个成员选择冲突的哪一方。网络的图示如下所示。每个节点代表一个空手道俱乐部的成员,成员之间的链接表明他们在俱乐部之外进行互动。管理员和讲师分别标有 A 和 I。
Zachary’s Karate Club
MXNet 中的谱图卷积
我在 MXNet 中实现了谱法则,这是一个易用且高效的深度学习框架。实现如下:
**class SpectralRule(HybridBlock):
def __init__(self,
A, in_units, out_units,
activation, **kwargs):
super().__init__(**kwargs) I = nd.eye(*A.shape)
A_hat = A.copy() + I D = nd.sum(A_hat, axis=0)
D_inv = D**-0.5
D_inv = nd.diag(D_inv) A_hat = D_inv * A_hat * D_inv
self.in_units, self.out_units = in_units, out_units
with self.name_scope():
self.A_hat = self.params.get_constant('A_hat', A_hat)
self.W = self.params.get(
'W', shape=(self.in_units, self.out_units)
)
if activation == 'ident':
self.activation = lambda X: X
else:
self.activation = Activation(activation) def hybrid_forward(self, F, X, A_hat, W):
aggregate = F.dot(A_hat, X)
propagate = self.activation(
F.dot(aggregate, W))
return propagate**
__init__
将邻接矩阵A
以及来自图卷积层的每个节点的特征表示的输入和输出维度作为输入;分别为in_units
和out_units
。通过与单位矩阵I
相加,将自循环添加到邻接矩阵A
中,计算度矩阵D
,并将邻接矩阵A
转换为谱规则指定的A_hat
。这种变换不是严格必需的,但是在计算上更有效,因为否则变换将在层的每次向前传递期间执行。
最后,在__init__
的with
子句中,我们存储了两个模型参数— A_hat
存储为常量,权重矩阵W
存储为可训练参数。
奇迹就发生在这里。在前向传递中,我们使用以下输入执行该方法:X
,前一层的输出,以及我们在构造函数__init__
中定义的参数A_hat
和W
。
构建图形卷积网络
现在我们有了光谱规则的实现,我们可以将这样的层堆叠在彼此之上。我们使用类似于上一篇文章中的两层架构,其中第一个隐藏层有 4 个单元,第二个隐藏层有 2 个单元。这种体系结构可以很容易地将产生的二维嵌入可视化。它与前一篇文章中的架构有三点不同:
- 我们使用光谱法则而不是平均值法则。
- 我们使用不同的激活函数:在第一层中使用 tanh 激活函数,因为否则死亡神经元的概率会非常高,并且第二层使用同一性函数,因为我们使用最后一层来分类节点。
最后,我们在 GCN 之上添加一个逻辑回归层用于节点分类。
上述架构的 Python 实现如下。
**def build_model(A, X):
model = HybridSequential() with model.name_scope():
features = build_features(A, X)
model.add(features) classifier = LogisticRegressor()
model.add(classifier) model.initialize(Uniform(1)) return model, features**
我已经将包含图卷积层的网络的特征学习部分分成了一个features
组件,将分类部分分成了classifier
组件。独立的features
组件使得稍后可视化这些层的激活更加容易。LogisticRegressor
作为分类层,通过对最后一个图形卷积层提供的每个节点的特征求和并对该和应用 sigmoid 函数来执行逻辑回归。
你可以在 中找到构造 *features*
组件的代码和 *LogisticRegressor*
组件的代码。
训练 GCN
用于训练 GCN 模型的代码如下所示。简而言之,我初始化一个二元交叉熵损失函数cross_entropy
和一个 SGD 优化器trainer
来学习网络参数。然后,该模型被训练特定数量的时期,其中为每个训练示例计算loss
,并且使用loss.backward()
反向传播误差。然后调用trainer.step
来更新模型参数。在每个时期之后,由 GCN 层构建的特征表示被存储在feature_representations
列表中,我们将很快对此进行检查。
**def train(model, features, X, X_train, y_train, epochs):
cross_entropy = SigmoidBinaryCrossEntropyLoss(from_sigmoid=True)
trainer = Trainer(
model.collect_params(), 'sgd',
{'learning_rate': 0.001, 'momentum': 1}) feature_representations = [features(X).asnumpy()] for e in range(1, epochs + 1):
for i, x in enumerate(X_train):
y = array(y_train)[i]
with autograd.record():
pred = model(X)[x] # Get prediction for sample x
loss = cross_entropy(pred, y)
loss.backward()
trainer.step(1) feature_representations.append(features(X).asnumpy()) return feature_representations**
至关重要的是,只有教师和管理员的标签被标记,网络中的其余节点是已知的,但未被标记!GCN 可以在图卷积期间找到标记和未标记节点的表示,并且可以在训练期间利用这两种信息源来执行半监督学习。
具体来说,半监督学习发生在 GCN 中,因为它通过聚集节点的标记和未标记邻居来产生节点的潜在特征表示。在训练期间,我们然后反向传播监督的二进制交叉熵损失,以更新所有节点共享的权重。然而,这种损失取决于标记节点的潜在特征表示,而潜在特征表示又取决于标记节点和未标记节点。因此,学习变成半监督的。
可视化特征
如上所述,存储每个时期的特征表示,这允许我们看到特征表示在训练期间如何变化。在下文中,我考虑两种输入特征表示。
表示 1 在第一种表示中,我们简单地用稀疏的 34 × 34 单位矩阵, I 作为特征矩阵X
,即对图中每个节点的一键编码。这种表示的优点在于,它可以用于任何图中,但是导致网络中每个节点的输入参数,这需要大量的存储器和计算能力用于大型网络上的训练,并且可能导致过拟合。谢天谢地,空手道俱乐部网络相当小。使用这种表示对网络进行 5000 个时期的训练。**
Classification Errors in the Karate Club using Representation 1
通过对网络中的所有节点进行集体分类,我们得到了网络中的误差分布,如上图所示。这里,黑色表示分类错误。尽管有将近一半(41%)的节点被错误分类,但是与管理员或教师(但不是两者)都有密切联系的节点。)倾向于正确分类。
Changes in Feature Representation during Training using Representation 1
在左侧,我已经说明了特征表示在训练过程中是如何变化的。节点最初紧密地聚集在一起,但是随着训练的进行,教师和管理员被拉开,拖着一些节点。
尽管管理员和教师被给予完全不同的表示,但是他们所拖动的节点不一定属于他们的社区。这是因为图卷积在特征空间中将共享邻居的节点紧密地嵌入在一起,但是共享邻居的两个节点可能不平等地连接到管理员和教师。特别地,使用单位矩阵作为特征矩阵导致每个节点的高度局部表示,即,属于图的相同区域的节点可能紧密地嵌入在一起。这使得网络难以以归纳的方式在遥远的区域之间共享公共知识。
表示 2
我们将通过添加两个不特定于网络的任何节点或区域的特征来改进表示 1,这两个特征测量与管理员和教师的连通性。为此,我们计算从网络中的每个节点到管理员和教师的最短路径距离,并将这两个特征连接到之前的表示。
On 可能会认为这是一种欺骗,因为我们注入了关于图中每个节点位置的全局信息;应该(理想地)由features
组件中的图形卷积层捕获的信息。然而,图卷积层总是具有局部视角,并且捕获这种信息的能力有限。尽管如此,它仍然是理解 gcn 的有用工具。
Classification Errors in the Karate Club using Representation 1
如前所述,我们对网络中的所有节点进行分类,并绘制网络中的误差分布图,如上图所示。这一次,只有四个节点被错误分类;相对于表示 1 的显著改进!根据对特征矩阵的更仔细的检查,这些节点或者与教师和管理员等距(在最短路径意义上),或者更靠近管理员但属于教师团体。使用表示 2 对 GCN 进行 250 个时期的训练。
Changes in Feature Representation during Training using Representation 2
如左图所示,节点最初还是非常紧密地聚集在一起,但是在训练开始之前就已经分成了几个社区!随着训练的进行,社区之间的距离增加了。
下一步是什么?
在这篇文章中,我已经深入解释了 gcn 中的聚合是如何执行的,并以均值、求和以及谱规则为例,展示了如何将其表示为加权和。我真诚的希望你会发现这个框架对于考虑在你自己的图卷积网络中聚合时你可能想要的权重是有用的。
我还展示了如何在 MXNet 中实现和训练一个 GCN,使用谱图卷积在图上执行半监督分类,并以 Zachary 的空手道俱乐部作为一个简单的示例网络。我们看到了仅使用两个带标签的节点,GCN 仍有可能在表示空间中实现两个网络社区之间的高度分离。
虽然还有很多关于图卷积网络的知识需要学习,我希望将来有时间与你分享,但这是(目前)这个系列的最后一篇文章。如果你对进一步阅读感兴趣,我想以下列我认为相当有趣的论文作为结束:
- 大型图上的归纳表示学习
在这篇论文中,Hamilton 等人提出了几个新的聚合函数,例如,使用 max/mean pooling 或多层感知器。此外,他们还提出了一种简单的方法对 GCNs 进行小批量训练,大大提高了训练速度。 - 陈等人。al 提出了他们的 FastGCN 方法,该方法通过独立地执行图卷积层的批量训练来解决这个缺点。
- N-GCN:用于半监督节点分类的多尺度图卷积
其中 FastGCN 解决了训练递归图卷积网络的问题,N-GCN 挑战了 GCNs 完全需要递归的前提!相反,Abu-El-Haija 等人提出了一种具有多个(N 个)gcn 的扁平架构,其输出被连接在一起。每个 GCN 在不同的距离捕获邻域(基于随机行走语义),从而避免递归聚集。感谢 Binny Mathew 让我注意到这一点。
喜欢你读的书吗?考虑在 Twitter 上关注我,在那里,除了我自己的帖子之外,我还分享与数据科学和机器学习的实践、理论和伦理相关的论文、视频和文章。
如需专业咨询,请在LinkedIn上联系我,或在Twitter上直接留言。
参考
[1] 论文名为带有图卷积网络的半监督分类作者 Thomas Kipf 和 Max Welling。
如何进行误差分析以使你所有的模型更好
循序渐进的指南
From Pexels
介绍
有很多方法可以改进开箱即用的机器学习模型。您可以对模型和数据做一些事情,以获得比标准 scikit-learn 版本的模型更好的结果。有特征工程、超参数调整和选择正确的成本函数。如果我们进入深度学习,那么我们接近网络架构、迁移学习和许多其他的想法。作为一名数据科学家,这些是你学习建模的第一步,也是最重要的一步。像随机搜索和网格搜索这样的工具应该在每个数据科学家的工具箱中,以帮助优化这一过程,并建立可能的最佳模型。
当你在一个项目中工作时,大多数人做了我上面提到的所有事情,然后,通过交叉验证或训练测试分割,获得一些评估指标。重复这些步骤,直到获得可能的最佳度量分数,并且认为该模型值得使用。这绝不是一条糟糕的道路。它通常会产生优秀的模型,以它们应该的方式提供价值。然而,只要付出一点额外的努力,你就能让你构建的每一个模型都变得更好。我们可以通过误差分析来实现这一点。
误差分析
这是什么?
当建立一个模型时,你可以随机地或者有意识地去做。建立模型的随机方法很容易被采用。它需要更少的努力,但仍然会带来结果。一种随机的方法是,在一个模型的每次迭代之后,你决定尝试一些新的东西来使它变得更好,而不去想为什么。很简单:一个可能会提高 F1 分数的想法突然出现在你的脑海中,为什么不试试呢?这很简单,也很有效。
https://imgs.xkcd.com/comics/machine_learning.png
建立模型的有意方法是使用误差分析。错误分析要求您在每次迭代后深入研究模型的结果。你在观察的层面上观察数据和预测,并形成假设,解释为什么你的模型在某些预测上失败了。然后,您通过以可能修复错误的方式更改模型来测试您的假设,并开始下一次迭代。随着误差分析的进行,建模的每一次迭代都变得更加耗时,但是最终的结果会更好,并且可能会更快到达。
进行误差分析的正确方法
没有一种固定的方法可以让你进行错误分析。它是探索性的,需要一些创造力和领域知识。也就是说,我会给你我通常喜欢采取的步骤。希望这能给你一个起点,让你从挖掘模型的错误中找到深刻的见解。
误差分析过程
对于模型的每一次迭代,我都从这些步骤的开头开始,并遍历它们,直到我找到应该修复的东西。我提出我的假设,改变我的模型,重新开始这个过程,直到我满意为止。在这一节中,我将向您介绍我采取的步骤,一些您可能会发现错误的地方,以及一些可能的修复方法。我们将从最高级别的数据到最低级别的数据,一路寻找错误。
数据级错误
这可能是最常见的错误分析类型,大多数人都这样做。我喜欢从观察所有数据的模型误差开始。
检查你的模型是否过度或不足。我最喜欢的方法是使用学习曲线,如下图所示。
Learning Curve from Scikit-Learn
一般来说,我在实践中发现,最好是先过度拟合,然后使用调节技术将模型带回到一个好的位置。
潜在修复:
- 如果过度拟合,则 L1 或 L2 正则化或更多数据
- 如果拟合不足,则需要更复杂的模型或更多的特征
预测分布
确保数据的分布看起来与预测的分布有些相似。如果它们看起来不相似,那我们就有问题了。
潜在修复:
- 从数据中移除异常值
- 使用不同的成本函数
组级错误
一旦数据级别的错误得到处理,我们就可以按组查看错误。这部分误差分析的目的是识别模型表现差的组。在决定如何创建组时,一些领域知识会有所帮助。我建议从数据中已经存在的明显的群体开始,然后根据直觉创建群体。例如,我可能会先通过分类变量来查看预测误差,然后再将数字变量拆分到多个箱中进行检查。在观察群体时,我总是会留意一些具体的事情。
预测过高/过低
查看是否有任何特定的组,该模型在很长时间内高估或低估了这些组。如果一个模型总是过度预测某个群体,这似乎是我们可以教它解决的问题。如果表现不佳的小组缺乏数据,也要注意。
潜在修复:
- 惩罚对该组过度预测
- 缺少数据时的过采样方法
- 如果缺少数据,则收集数据
平均误差大
查看是否有任何组比所有其他组对总误差的贡献更大。出于多种原因,这一点很重要。首先,我们显然想知道哪里出了问题,以便能够修复。第二,它关系到模型的可解释性和应用。如果一个模型在除了一个组之外的所有组上都表现良好,如果它不能被修复,您可能只想将该模型用于除该组之外的所有事情。
潜在修复:
- k-分层交叉验证,以确保每次分裂的群体观察
- 缺少数据时的过采样方法
- 数据收集缺乏数据
个别错误
现在我们开始讨论实质问题。一旦我们完成了所有更高层次的误差分析,是时候深入每个观察的误差了。我喜欢通过查看我预测的前 20 个错误来做到这一点。与更高层次的分析不同,我对您可能会发现的常见错误没有任何建议。你应该仔细观察,查看数据和预测,并确定预测是否有效。有时模型做出了很好的预测,而观察的目标却不可靠。这些是我们可以忍受的错误。如果你认为这个观察似乎不合理,问问自己为什么。模型遗漏了哪些本应帮助它做出更好预测的信息?通过多次观察寻找可能导致错误的模式。你可能会发现一些你以前忽略的东西,你可以修复。你可能会发现,有时候你的模型就是搞砸了,你找不到原因。从这一点出发,你要么继续调整你的模型,决定去获取更多的数据,要么称你的模型是好的。
实施它
错误分析需要时间,也需要大量的思考。花时间搜索和检查模型性能差的原因。好的错误分析模式是这样的:
- 查找错误
- 创建一个可以修复错误的假设
- 测试假设
- 重复
遵循这些步骤,加上一点创造力和耐心,将有助于您构建一个比单独进行一些超参数调整更好的模型。
如果你有任何关于如何进行有效的错误分析的问题,请在下面给我留言,我会回答的!
如何使用 Python 对 Pandas Dataframe 列进行模糊匹配?
熊猫和 FuzzyWuzzy 的模糊字符串匹配
模糊字符串匹配或搜索是近似匹配特定模式的字符串的过程。这是一个非常受欢迎的 Excel 附件。它给出了一个近似的匹配,并不能保证字符串是精确的,但是,有时字符串会精确地匹配模式。字符串与给定匹配的接近程度由编辑距离来衡量。FuzzyWuzzy 使用 Levenshtein 距离 来计算编辑距离。
如何安装 FuzzyWuzzy 包
要安装 FuzzyWuzzy,您可以使用如下的 pip 命令
Pip install fuzzywuzzy
Pip install python-Levenshtein
人们也可以使用 conda 来安装 FuzzyWuzzy。以下命令将安装该库。
**conda install -c conda-forge fuzzywuzzy
conda install -c conda-forge python-levenshtein**
现在让我们导入这些包,因为我们已经成功地安装了上面提到的库。
from **fuzzywuzzy** import **fuzz**
from **fuzzywuzzy** import **process**
现在,让我告诉你如何使用它。
In[1]:fuzz.ratio(“Sankarshana Kadambari”,”Sankarsh Kadambari”)
Out[1]:92
In[2]:fuzz.partial_ratio("Sankarshana Kadambari","Sankarsh Kadambari")
Out[2]:83
Fuzz 有各种方法可以比较字符串,比如 ratio()、partial_ratio()、Token_Sort_Ratio()、Token_Set_Ratio()。现在下一个问题是什么时候使用哪个模糊函数。你可以在这里阅读这些场景都有很好的解释。此外,我将在最后提供参考,在那里它将与代码一起被进一步详细解释。
现在博客的议程如何用这个在熊猫两栏之间的数据框然后导出到 excel?
import pandas as pd
from fuzzywuzzy import fuzz
from fuzzywuzzy import processdef checker(wrong_options,correct_options):
names_array=[]
ratio_array=[]
for wrong_option in wrong_options:
if wrong_option in correct_options:
names_array.append(wrong_option)
ratio_array.append(‘100’)
else:
x=process.
extractOne(wrong_option,correct_options,scorer=fuzz.token_set_ratio)
names_array.append(x[0])
ratio_array.append(x[1])
return names_array,ratio_array
在上面的代码片段中,我使用了 token_set_ratio,因为它符合我的要求。我还添加了一个 if 块,通过检查第二列中的名称来减少迭代次数,因为有时出现的可能性很大。你可以在 scorer 参数中尝试各种其他方法,我在底部分享了源代码,在那里可以详细研究剩余模糊方法的工作。
我不会说这是实现模糊逻辑的唯一方法,但是您也可以自己尝试如何在减少执行时间的同时增加代码的可伸缩性。
现在让我们传递方法中的参数,并创建一个包含结果的输出 excel 文件。
df_Original_List=pd.read_excel(“Original_list.xlsx”,sheet_name=’Sheet1',usecols=[“Original Name Column”])df_To_beMatched=pd.read_excel("To be matched.xlsx",sheet_name='Sheet1',usecols=["To be Matched Column"])
为了避免错误,在将列传递给函数之前,清理列是非常重要的。主要的运行时错误是由列中的 NAN 值创建的。我用以下方式处理它们。
str2Match = df_To_beMatched[‘To be Matched Column’].fillna(‘######’).tolist()
strOptions =df_Original_List[‘Original Name Column’].fillna(‘######’).tolist()
现在让我们传递方法中的参数。
name_match,ratio_match=checker(str2Match,strOptions)df1 = pd.DataFrame()
df1[‘old_names’]=pd.Series(str2Match)
df1[‘correct_names’]=pd.Series(name_match)
df1[‘correct_ratio’]=pd.Series(ratio_match)
df1.to_excel(‘matched_names.xlsx’, engine=’xlsxwriter’)
瞧,就是这样。我希望这篇文章在你各自的领域对你有用。这是一个在 excel 中使用的非常强大的函数,但现在它也可以在 python 中用于文本分析或分析。
您可以在其他资源中找到已实现的剩余方法及其各自的工作方式。
- https://medium . com/@ categitau/fuzzy-string-matching-in-python-68f 240d 910 Fe
- https://www.geeksforgeeks.org/fuzzywuzzy-python-library/
- https://www . data camp . com/community/tutorials/fuzzy-string-python
- https://towardsdatascience . com/natural-language-processing-for-fuzzy-string-matching-with-python-6632 b 7824 c 49
- https://galaxydatatech . com/2017/12/31/fuzzy-string-matching-pandas-fuzzywuzzy/
如何对 BigQuery ML 模型进行超参数调优
使用云 AI 平台的贝叶斯优化或使用脚本的网格搜索
注意:超参数调优 现在已经内置到 BigQuery ML 中,对于简单的调优需求,您可以简单地指定一个 hparam_range。使用本文中的方法进行更复杂的超参数调优。
在进行机器学习时,有许多参数我们选择得相当随意。这些包括诸如学习速率、L2 正则化的水平、神经网络中的层数和节点数、提升树的最大深度、矩阵分解模型的因子数等因素。通常情况下,为这些值选择不同的值可能会产生更好的模型(通过保留的评估数据集上的误差来衡量)。为这些参数选择一个好的值称为超参数调整。
下面是 GitHub 中示例的完整代码。它来自我们即将出版的关于 BigQuery 的书。
The code here is from Chapter 9 of our new book on BigQuery. You can read it in early access on Safari.
使用脚本调整超参数
以 K 均值聚类模型为例。BigQuery 云控制台中的 evaluation 选项卡(以及 SELECT * from ML。EVALUATE)显示了 Davies-Bouldin 指数,该指数有助于确定数据支持的最佳分类数(数字越小,分类越好)。
例如,下面是一个尝试改变聚类数量的脚本:
DECLARE NUM_CLUSTERS INT64 DEFAULT 3;
DECLARE MIN_ERROR FLOAT64 DEFAULT 1000.0;
DECLARE BEST_NUM_CLUSTERS INT64 DEFAULT -1;
DECLARE MODEL_NAME STRING;
DECLARE error FLOAT64 DEFAULT 0;WHILE NUM_CLUSTERS < 8 DOSET MODEL_NAME = CONCAT('ch09eu.london_station_clusters_',
CAST(NUM_CLUSTERS AS STRING));EXECUTE IMMEDIATE format("""
CREATE OR REPLACE MODEL %s
OPTIONS(model_type='kmeans',
num_clusters=%d,
standardize_features = true) AS
SELECT * except(station_name)
from ch09eu.stationstats;
""", MODEL_NAME, NUM_CLUSTERS);
EXECUTE IMMEDIATE format("""
SELECT davies_bouldin_index FROM ML.EVALUATE(MODEL %s);
""", MODEL_NAME) INTO error;
IF error < MIN_ERROR THEN
SET MIN_ERROR = error;
SET BEST_NUM_CLUSTERS = NUM_CLUSTERS;
END IF;
SET NUM_CLUSTERS = NUM_CLUSTERS + 1;END WHILE
它使用动态 SQL(立即执行)为每个尝试的 num_clusters 创建单独的模型名称。
Python 中超参数调优
或者,我们可以使用 Python 及其多线程功能来限制并发查询的数量:
def train_and_evaluate(num_clusters: Range, max_concurrent=3):
# grid search means to try all possible values in range
params = []
for k in num_clusters.values():
params.append(Params(k))
# run all the jobs
print('Grid search of {} possible parameters'.format(len(params)))
pool = ThreadPool(max_concurrent)
results = pool.map(lambda p: p.run(), params)
# sort in ascending order
return sorted(results, key=lambda p: p._error)
其中 Params 类的 run()方法调用适当的训练和评估查询:
class Params:
def __init__(self, num_clusters):
self._num_clusters = num_clusters
self._model_name = 'ch09eu.london_station_clusters_{}'.format(num_clusters)
self._train_query = """
CREATE OR REPLACE MODEL {}
OPTIONS(model_type='kmeans',
num_clusters={},
standardize_features = true) AS
SELECT * except(station_name)
from ch09eu.stationstats
""".format(self._model_name, self._num_clusters)
self._eval_query = """
SELECT davies_bouldin_index AS error
FROM ML.EVALUATE(MODEL {});
""".format(self._model_name)
self._error = None
def run(self):
bq = bigquery.Client(project=PROJECT)
job = bq.query(self._train_query, location='EU')
job.result() # wait for job to finish
evaldf = bq.query(self._eval_query, location='EU').to_dataframe()
self._error = evaldf['error'][0]
return self
当在范围[3,9]中搜索时,我们发现误差最小的聚类数是 7。
使用人工智能平台的超参数调谐
在迄今为止考虑的两种超参数调优方法中,我们尝试了一个范围内参数的每个可能值。随着可能参数数量的增加,网格搜索变得越来越浪费。最好使用更有效的搜索算法,这就是云人工智能平台的超参数调整可以发挥作用的地方。超参数调优服务可以用于任何模型(不仅仅是 TensorFlow)。让我们应用于调整 DNN 模型的特征工程和节点数量。
首先,我们创建一个配置文件,指定每个参数的范围、并发查询的数量以及试验的总数:
trainingInput:
scaleTier: CUSTOM
masterType: standard # See: [https://cloud.google.com/ml-engine/docs/tensorflow/machine-types](https://cloud.google.com/ml-engine/docs/tensorflow/machine-types)
hyperparameters:
goal: MINIMIZE
** maxTrials: 50**
maxParallelTrials: 2
hyperparameterMetricTag: mean_absolute_error
params:
- parameterName: afternoon_start
type: INTEGER
minValue: 9
maxValue: 12
scaleType: UNIT_LINEAR_SCALE
- parameterName: afternoon_end
type: INTEGER
minValue: 15
maxValue: 19
scaleType: UNIT_LINEAR_SCALE
- parameterName: num_nodes_0
type: INTEGER
minValue: 10
maxValue: 100
scaleType: UNIT_LOG_SCALE
- parameterName: num_nodes_1
type: INTEGER
minValue: 3
maxValue: 10
scaleType: UNIT_LINEAR_SCALE
请注意,我们已经为每个参数和要最小化的度量(平均绝对误差)指定了最小值和最大值。我们要求优化只需要 50 次尝试,而网格搜索需要尝试 4x4x90x7 或超过 10,000 个选项!因此,使用 AI 平台超参数调整服务可以节省 200 倍的成本!
然后,我们创建一个 Python 程序,该程序调用 BigQuery 来训练和评估给定一组参数的模型:
def train_and_evaluate(args):
model_name = "ch09eu.bicycle_model_dnn_{}_{}_{}_{}".format(
args.afternoon_start, args.afternoon_end, args.num_nodes_0, args.num_nodes_1
)
train_query = """
CREATE OR REPLACE MODEL {}
TRANSFORM(* EXCEPT(start_date)
, IF(EXTRACT(dayofweek FROM start_date) BETWEEN 2 and 6, 'weekday', 'weekend') as dayofweek
, ML.BUCKETIZE(EXTRACT(HOUR FROM start_date), [5, {}, {}]) AS hourofday
)
OPTIONS(input_label_cols=['duration'],
model_type='dnn_regressor',
hidden_units=[{}, {}])
ASSELECT
duration
, start_station_name
, start_date
FROM `bigquery-public-data`.london_bicycles.cycle_hire
""".format(model_name,
args.afternoon_start,
args.afternoon_end,
args.num_nodes_0,
args.num_nodes_1)
logging.info(train_query)
bq = bigquery.Client(project=args.project,
location=args.location,
credentials=get_credentials())
job = bq.query(train_query)
job.result() # wait for job to finish
eval_query = """
SELECT mean_absolute_error
FROM ML.EVALUATE(MODEL {})
""".format(model_name)
logging.info(eval_info)
evaldf = bq.query(eval_query).to_dataframe()
return evaldf['mean_absolute_error'][0]
请注意,上面的代码为每个可调参数使用了一个特定的值,并返回平均绝对误差,这是被最小化的度量。
然后写出该错误值:
hpt.report_hyperparameter_tuning_metric(
hyperparameter_metric_tag='mean_absolute_error',
metric_value=error,
global_step=1)
培训计划提交给 AI 平台培训服务:
gcloud ai-platform jobs submit training $JOBNAME \
--runtime-version=1.13 \
--python-version=3.5 \
--region=$REGION \
--module-name=trainer.train_and_eval \
--package-path=$(pwd)/trainer \
--job-dir=gs://$BUCKET/hparam/ \
--config=hyperparam.yaml \
-- \
--project=$PROJECT --location=EU
在 AI 平台控制台中显示的结果输出包含最佳参数。
尽情享受吧!
如何用 gKnit 在 Ruby 中进行可重复的研究
罗德里戈·博塔弗戈和丹尼尔·莫斯
[编辑:]从 gala az 0 . 4 . 10 版本开始,gKnit 将允许代码块之间的局部变量,感谢 Benoit 的评论和建议。
介绍
“有文化的编程”的概念是由 Donald Knuth 在 20 世纪 80 年代首次提出的(Knuth 1984)。这种方法的主要目的是开发在文档中散布宏代码片段、传统源代码和自然语言(如英语)的软件,该文档可以被编译成可执行代码,同时易于人类开发人员阅读。根据 Knuth 的说法,“有文化的编程实践者可以被认为是一个散文家,他主要关心的是阐述和优秀的风格。”
文字编程的想法演变成了可复制研究的想法,所有的数据、软件代码、文档、图形等等。需要复制的研究及其报告可以包含在单个文档或一组文档中,当分发给同行时,可以重新运行这些文档,生成相同的输出和报告。
R 社区在可重复的研究上投入了大量的努力。2002 年,Sweave 被引入,它允许混合 R 代码和 Latex 生成高质量的 PDF 文档。Sweave 文档可以包括代码、执行代码的结果、图形和文本,这样它就包含了再现研究的全部叙述。2012 年,RStudio 的 Yihui Xie 开发的 Knitr 发布,取代了 Sweave,并将 Sweave 所需的许多扩展和附加包整合到一个软件包中。
对于 Knitr, R markdown 也被开发出来,它是 markdown 格式的扩展。使用 R markdown 和 Knitr,可以生成多种格式的报告,如 HTML、markdown、Latex、PDF、dvi 等。 R markdown 还允许使用多种编程语言,如 R、Ruby、Python 等。在同一份文件中。
在 R markdown 中,文本与可以执行的代码块穿插在一起,代码及其结果都可以成为最终报告的一部分。虽然 R markdown 允许在同一个文档中使用多种编程语言,但是只有 R 和 Python(带有 reticulate 包)可以在块之间持久化变量。对于其他语言,比如 Ruby,每个块都将启动一个新的进程,因此所有数据都会在块之间丢失,除非它以某种方式存储在下一个块读取的数据文件中。
能够在块之间持久存储数据对于有文化的编程来说是至关重要的,否则叙事的流程会因为必须保存数据然后重新加载数据而丢失。虽然这看起来有点麻烦,但是不能在块之间持久化数据是一个大问题。例如,让我们看看下面这个简单的例子,在这个例子中我们想展示如何创建一个列表并使用它。让我们首先假设数据不能在块之间持久化。在下一个块中,我们创建一个列表,然后我们需要将它保存到文件中,但是为了保存它,我们需要以某种方式将数据整理成二进制格式:
lst = R.list(a: 1, b: 2, c: 3)
lst.saveRDS("lst.rds")
然后,在下一个使用变量’ lst '的块中,我们需要读回它的值
lst = R.readRDS("lst.rds")
puts lst## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
现在,任何单个代码都有几十个变量,我们可能希望在块之间使用和重用这些变量。显然,这种方法很快变得难以控制。大概就是因为这个问题,在 Ruby 社区很少看到任何 R markdown 文档。
当变量可以跨块使用时,就不需要开销:
@lst = R.list(a: 1, b: 2, c: 3)
*# any other code can be added here*puts @lst## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
在 Python 社区中,在 2000 年的第一个十年左右,开始了将代码和文本集成到一个集成环境中的努力。2006 年,iPython 0.7.2 发布。2014 年,费尔南多·佩雷斯将 iPython 的 Jupyter 项目剥离出来,创建了一个基于网络的交互式计算环境。Jupyter 现在可以用于许多语言,包括带有 iruby gem 的 Ruby(【https://github.com/SciRuby/iruby】T2)。为了在 Jupyter 笔记本中拥有多种语言,SoS 内核被开发出来(【https://vatlab.github.io/sos-docs/】T4)。
编织文件
本文档描述了 gKnit。gKnit 基于 knitr 和 R markdown ,可以编织一个用 Ruby 和/或 R 编写的文档,并以 R markdown 的任何可用格式输出。gKnit 允许 ruby 开发人员在单个文档、文本和代码中进行有文化的编程和可重复的研究。
gKnit 运行在 GraalVM 和 gala az(Ruby 和 R 之间的集成库——见下文)之上。在 gKnit 中,Ruby 变量在块之间持久化,这使得它成为用这种语言进行文化编程的理想解决方案。此外,由于它是基于 Galaaz 的,Ruby 块可以访问 R 变量,用 Ruby 和 R 进行多语言编程是非常自然的。
Galaaz 已经在下面的帖子中描述过了:
- https://towardsdatascience . com/ruby-plotting-with-gala az-a-example of-tight-coupling-ruby-and-r-in-graalvm-520 b 69 e 21021。
- https://medium . freecodecamp . org/how-to-make-beautiful-ruby-plots-with-gala az-320848058857
这不是一篇关于 R markdown 的博文,感兴趣的用户可以通过下面的链接获得关于其功能和使用的详细信息。
- 【https://rmarkdown.rstudio.com/】T4 或者
- https://bookdown.org/yihui/rmarkdown/
在这篇文章中,我们将只描述 R markdown 的主要方面,这样用户就可以快速开始 gKnitting Ruby 和 R 文档。
Yaml 标头
一个 R markdown 文档应该以一个 Yaml 头开始,并保存在一个带有’的文件中。Rmd '扩展。这个文档有下面的标题,用来打包一个 HTML 文档。
---
title: "How to do reproducible research in Ruby with gKnit"
author:
- "Rodrigo Botafogo"
- "Daniel Mossé - University of Pittsburgh"
tags: [Tech, Data Science, Ruby, R, GraalVM]
date: "20/02/2019"
output:
html_document:
self_contained: true
keep_md: true
pdf_document:
includes:
in_header: ["../../sty/galaaz.sty"]
number_sections: yes
---
有关 Yaml 标题中选项的更多信息,请查看https://bookdown.org/yihui/rmarkdown/html-document.html。
r 降价格式
文档格式可以通过简单的标记来完成,例如:
头球
# Header 1## Header 2### Header 3
列表
Unordered lists:* Item 1
* Item 2
+ Item 2a
+ Item 2bOrdered Lists1\. Item 1
2\. Item 2
3\. Item 3
+ Item 3a
+ Item 3b
更多 R 减价格式,请转到https://rmarkdown.rstudio.com/authoring_basics.html。
r 组块
运行和执行 Ruby 和 R 代码实际上是我们真正感兴趣的是这个博客。
插入代码块的方法是将代码添加到由三个反勾号分隔的块中,后接左大括号(’ { '),后接引擎名(r、ruby、rb、include、…)、任何可选的 chunk_label 和选项,如下所示:
```{engine_name [chunk_label], [chunk_options]}
例如,让我们向标记为‘first _ R _ chunk’的文档添加一个 R chunk。这是一个非常简单的代码,只需创建一个变量并将其打印出来,如下所示:
vec <- c(1, 2, 3)
print(vec)
如果该块被添加到一个 **R 降价**文件中,结果将是:
vec <- c(1, 2, 3)
print(vec)## [1] 1 2 3
现在假设我们想在代码中做一些分析,但是只打印结果而不是代码本身。为此,我们需要添加选项“echo = FALSE”。
vec2 <- c(10, 20, 30)
vec3 <- vec * vec2
print(vec3)
下面是这个块在文档中的显示方式。注意没有显示代码,我们只能在一个白色的框中看到执行结果
[1] 10 40 90
可用组块选项的描述可在[https://yihui.name/knitr/](https://yihui.name/knitr/)中找到。
让我们添加另一个带有函数定义的 R 块。在本例中,创建了一个向量“r_vec ”,并定义了一个新函数“reduce_sum”。区块规范是
r_vec <- c(1, 2, 3, 4, 5)reduce_sum <- function(...) {
Reduce(sum, as.list(...))
}
这是它执行后的样子。从现在开始,为了简洁起见,我们将不再展示组块定义。
r_vec <- c(1, 2, 3, 4, 5)reduce_sum <- function(…) {
Reduce(sum, as.list(…))
}
我们可以,可能在另一个块中,访问向量并如下调用函数:
print(r_vec)## [1] 1 2 3 4 5print(reduce_sum(r_vec))## [1] 15
# 带 ggplot 的 r 图形
在下面的块中,我们使用 ggplot 在 R 中创建了一个气泡图,并将其包含在本文档中。请注意,代码中没有包含图像的指令,这是自动发生的。“mpg”数据帧对于 R 和 Galaaz 来说也是固有可用。
对于不了解 ggplot 的读者,ggplot 是一个基于“图形语法”(Wilkinson 2005)的图形库。图形语法的思想是通过给情节添加层来构建图形。更多信息可以在[https://towardsdatascience . com/a-comprehensive-guide-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1 f 92 B4 ed 4149](/a-comprehensive-guide-to-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1f92b4ed4149)中找到。
在下图中,使用了来自基数 R 的“mpg”数据集。数据涉及城市循环油耗,单位为英里/加仑,根据 3 个多值离散属性和 5 个连续属性进行预测(昆兰,1993 年)
首先,过滤“mpg”数据集,仅从以下制造商提取汽车:奥迪、福特、本田和现代,并存储在“mpg_select”变量中。然后,选定的数据帧被传递到 ggplot 函数,该函数在美学方法(aes)中指定“位移”(disp)应绘制在“x”轴上,而“城市里程”应绘制在“y”轴上。在“实验室”层中,我们为情节传递“标题”和“副标题”。在基本图“g”中,增加了 geom_jitter,它用相同的颜色(col=manufactures)绘制来自相同制造商的汽车,汽车点的大小等于其高速公路消耗(size = hwy)。最后,最后一层是绘图仪,包含每个制造商的线性回归线(method = "lm ")。
# load package and data
library(ggplot2)
data(mpg, package=“ggplot2”)mpg_select <- mpg[mpg$manufacturer %in% c(“audi”, “ford”, “honda”, “hyundai”), ]# Scatterplot
theme_set(theme_bw()) # pre-set the bw theme.
g <- ggplot(mpg_select, aes(displ, cty)) +
labs(subtitle=“mpg: Displacement vs City Mileage”,
title=“Bubble chart”)g + geom_jitter(aes(col=manufacturer, size=hwy)) +
geom_smooth(aes(col=manufacturer), method=“lm”, se=F)

# 红宝石块
在文档中包含一个 ruby 块就像包含一个 R 块一样简单:只需将引擎的名称改为“Ruby”。也可以将块选项传递给 Ruby 引擎;但是,这个版本不接受 R chunks 可用的所有选项。未来的版本将增加这些选项。
在这个例子中,ruby 块被称为‘first _ ruby _ chunk’。组块标签的一个重要方面是它们不能被复制。如果一个块标签被复制,gKnit 将会出错停止。
Ruby 块的另一个要点是,它们是在一个名为 RubyChunk 的类的范围内进行计算的。为了确保变量在块之间可用,应该将它们作为 RubyChunk 类的实例变量。在下面的块中,变量' @a '、' @b '和' @c '是标准的 Ruby 变量,而' @vec '和' @vec2 '是通过调用 R 模块上的' c '方法创建的两个向量。
在 Galaaz 中,R 模块允许我们透明地访问 R 函数。R 中的“c”函数是一个将其参数连接起来构成向量的函数。
需要明确的是,gknit 中并没有要求调用或使用任何 R 函数。gKnit 会编织标准的 Ruby 代码,甚至是没有任何代码的一般文本。
@a = [1, 2, 3]
@b = “US$ 250.000”
@c = “The ‘outputs’ function”@vec = R.c(1, 2, 3)
@vec2 = R.c(10, 20, 30)
在下一个块中,使用并打印了变量' @a ',' @vec '和' @vec2 '。
puts @a
puts @vec * @vec2## [1, 2, 3]
[1] 10 40 90
注意,@a 是一个标准的 Ruby 数组,@vec 和@vec2 是相应的向量,其中乘法按预期工作。
# 从 Ruby 访问 R
GraalVM 上的 Galaaz 的一个优点是,可以很容易地从 Ruby 访问 R 中定义的变量和函数。下一个块从 R 中读取数据,并使用前面定义的“reduce_sum”函数。要从 Ruby 访问 R 变量,应该对代表 R 变量的 Ruby 符号应用' ~ '函数。因为 R 变量被称为“r_vec ”,所以在 Ruby 中,访问它的符号是“:r_vec ”,因此“~:r_vec”检索变量的值。
puts ~:r_vec## [1] 1 2 3 4 5
为了调用 R 函数,‘R .’模块的用法如下
puts R.reduce_sum(~:r_vec)## [1] 15
# 红宝石绘图
我们已经看到了一个用 r 绘图的例子。用 Ruby 绘图与用 r 绘图没有任何不同。在下面的例子中,我们使用 r 中的“mtcars”数据框架绘制了一个发散条形图。该数据摘自 1974 年的《美国汽车趋势》杂志,包括 32 辆汽车(1973-74 年款)的油耗和汽车设计与性能的 10 个方面。这十个方面是:
* mpg:英里/(美国)加仑
* cyl:气缸数量
* disp:排量(立方英寸)
* 马力:总马力
* drat:后桥传动比
* 重量:重量(1000 磅)
* qsec: 1/4 英里时间
* vs:发动机(0 = V 形,1 =直线)
* am:变速器(0 =自动,1 =手动)
* 档位:前进档的数量
* 碳水化合物:化油器数量
# copy the R variable :mtcars to the Ruby mtcars variable
@mtcars = ~:mtcars*# create a new column ‘car_name’ to store the car names so that it can be*
# used for plotting. The ‘rownames’ of the data frame cannot be used as
# data for plotting
@mtcars.car_name = R.rownames(:mtcars)# compute normalized mpg and add it to a new column called mpg_z
# Note that the mean value for mpg can be obtained by calling the ‘mean’
# function on the vector ‘mtcars.mpg’. The same with the standard
# deviation ‘sd’. The vector is then rounded to two digits with ‘round 2’
@mtcars.mpg_z = ((@mtcars.mpg - @mtcars.mpg.mean)/@mtcars.mpg.sd).round 2*# create a new column ‘mpg_type’. Function ‘ifelse’ is a vectorized function*
# that looks at every element of the mpg_z vector and if the value is below
# 0, returns ‘below’, otherwise returns ‘above’
@mtcars.mpg_type = (@mtcars.mpg_z < 0).ifelse(“below”, “above”)# order the mtcar data set by the mpg_z vector from smaler to larger values
@mtcars = @mtcars[@mtcars.mpg_z.order, :all]# convert the car_name column to a factor to retain sorted order in plot
@mtcars.car_name = @mtcars.car_name.factor levels: @mtcars.car_name*# let’s look at the first records of the final data frame*
puts @mtcars.head

require ‘ggplot’
puts @mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
R.geom_bar(E.aes(fill: :mpg_type), stat: ‘identity’, width: 0.5) +
R.scale_fill_manual(name: ‘Mileage’,
labels: R.c(‘Above Average’,
‘Below Average’),
values: R.c(‘above’: ‘#00ba38’,
‘below’: ‘#f8766d’)) +
R.labs(subtitle: “Normalised mileage from ‘mtcars’”,
title: “Diverging Bars”) +
R.coord_flip

# 内嵌 Ruby 代码
当使用 Ruby 块时,代码和输出被格式化成块,如上所示。这种格式并不总是需要的。有时,我们希望把 Ruby 评估的结果放在一个短语的中间。gKnit 允许用“rb”引擎添加内联 Ruby 代码。以下块规范将创建并内联拼音文本:
This is some text with inline Ruby accessing variable @b which has value:
and is followed by some other text!
这是一些带有内联 Ruby 访问变量@b 的文本,该变量的值为:US$ 250.000,后面是一些其他文本!
请注意,如果我们希望所有内容都在一行中,请不要在代码块之前或之后添加任何新行,这一点很重要,这会导致以下带有内联 Ruby 代码的句子。
# “输出”功能
他之前在 Ruby 块中使用了标准的“puts”方法来产生输出。“puts”的结果,如前面所有使用它的块中所见,被格式化在代码块后面的一个白盒中。然而,很多时候,我们希望在 Ruby 块中进行一些处理,并让这个处理的结果生成并输出到文档中,就像我们在 **R markdown** 文档中输入它一样。
例如,假设我们想在文档中创建一个新的标题,但是标题短语是一些代码处理的结果:也许它是我们将要阅读的文件的第一行。方法“outputs”添加其输出,就像在 **R markdown** 文档中键入一样。
现在看看变量' @c '(它在上面的前一个块中被定义为' @ c = ' outputs '函数)。“‘outputs’函数”实际上是这个部分的名称,它是使用 Ruby 块中的‘outputs’函数创建的。
生成这个标题的 ruby 块是:
outputs "### #{@c}"
三个“###”是我们在 **R markdown** 中添加标题 3 的方式。
# Ruby 块的 HTML 输出
我们刚刚看到了使用方法‘outputs’向 **R markdown** 文档添加文本。这种技术也可以用于向文档中添加 HTML 代码。在 **R markdown** 中,任何直接在文档中键入的 html 代码都会被正确呈现。
例如,下面是一个 HTML 格式的表定义及其在文档中的输出:
Firstname | Lastname | Age |
---|---|---|
Jill | Smith | 50 |
Eve | Jackson | 94 |
first name lastnameagejillsmith 50 eve Jackson 94
但是,手动创建 HTML 输出并不总是容易或可取的,特别是如果我们希望文档以其他格式呈现,例如 Latex。还有,上表看起来很丑。“kableExtra”库是一个创建漂亮表格的好库。看看https://cran . r-project . org/web/packages/kable extra/vignettes/awesome _ table _ in _ html . html
在下一个块中,我们以一个格式良好的表输出 R 中的“mtcars”数据帧。注意,我们通过使用“~:mtcars”来检索 mtcars 数据帧。
R.install_and_loads('kableExtra')
outputs (~:mtcars).kable.kable_styling
在块中包含 Ruby 文件
r 是一种为统计学家使用起来简单快捷的语言。据我所知,它不是一种用于开发大型系统的语言。当然,R 中有大型系统和库,但是这种语言的重点是开发统计模型并将其分发给其他人。
另一方面,Ruby 是用于大型软件开发的语言。用 Ruby 编写的系统会有几十个、几百个甚至几千个文件。要用有文化的编程来记录一个大系统,我们不能期望开发人员在一个文件中添加所有的文件。Rmd '文件。gKnit 提供了“include”块引擎来包含一个 Ruby 文件,就像在。Rmd '文件。
要包含一个文件,应该创建下面的块,其中是要包含的文件的名称,而扩展名是。Rb’,不需要添加。如果不包括“relative”选项,则它被视为 TRUE。当“relative”为真时,ruby 的“require_relative”语义用于加载文件,当“relative”为假时,搜索 Ruby 的$LOAD_PATH 以找到文件,并且它是“require”d。
```{include <filename>, relative = <TRUE/FALSE>}
下面我们包括文件“model.rb”,它在这个博客的同一个目录中。这段代码使用 R 'caret '包来分割一个训练集和测试集中的数据集。“caret”软件包是一个非常重要的用于进行数据分析的有用的软件包,它有数百个函数用于数据分析工作流程的所有步骤。仅仅使用“插入符号”来分割数据集就像使用众所周知的大炮来杀死苍蝇一样。我们在这里使用它只是为了说明,对于 Galaaz 来说,集成 Ruby 和 R,甚至使用一个非常复杂的包作为‘caret’都是微不足道的。
给你一个建议:‘caret’包有很多依赖项,在 Linux 系统中安装它是一个耗时的操作。如果软件包尚未安装,方法“R.install_and_loads”将安装该软件包,这可能需要一段时间。
```require 'galaaz'# Loads the R 'caret' package. If not present, installs it
R.install_and_loads 'caret'class Model
attr_reader :data
attr_reader :test
attr_reader :train #==========================================================
#
#==========================================================
def initialize(data, percent_train:, seed: 123) R.set__seed(seed)
@data = data
@percent_train = percent_train
@seed = seed
end #==========================================================
#
#========================================================== def partition(field) train_index =
R.createDataPartition(@data.send(field), p: @percet_train,
list: false, times: 1)
@train = @data[train_index, :all]
@test = @data[-train_index, :all]
end
endmtcars = ~:mtcars
model = Model.new(mtcars, percent_train: 0.8)
model.partition(:mpg)
puts model.train.head
puts model.test.head
记录宝石
gKnit 还允许开发人员记录和加载不在同一目录下的文件。Rmd '文件。
下面是一个从 TruffleRuby 加载“find.rb”文件的例子。在这个例子中,relative 被设置为 FALSE,所以 Ruby 将在它的$LOAD_PATH 中查找文件,用户不需要查找它的目录。
```{include find, relative = FALSE}
```# frozen_string_literal: true
#
# find.rb: the Find module for processing all files under a given directory.
##
# The +Find+ module supports the top-down traversal of a set of file paths.
#
# For example, to total the size of all files under your home directory,
# ignoring anything in a "dot" directory (e.g. $HOME/.ssh):
#
# require 'find'
#
# total_size = 0
#
# Find.find(ENV["HOME"]) do |path|
# if FileTest.directory?(path)
# if File.basename(path)[0] == ?.
# Find.prune # Don't look any further into this directory.
# else
# next
# end
# else
# total_size += FileTest.size(path)
# end
# end
#
module Find #
# Calls the associated block with the name of every file and directory listed
# as arguments, then recursively on their subdirectories, and so on.
#
# Returns an enumerator if no block is given.
#
# See the +Find+ module documentation for an example.
#
def find(*paths, ignore_error: true) # :yield: path
block_given? or return enum_for(__method__, *paths, ignore_error: ignore_error) fs_encoding = Encoding.find("filesystem") paths.collect!{|d| raise Errno::ENOENT, d unless File.exist?(d); d.dup}.each do |path|
path = path.to_path if path.respond_to? :to_path
enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding
ps = [path]
while file = ps.shift
catch(:prune) do
yield file.dup.taint
begin
s = File.lstat(file)
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
raise unless ignore_error
next
end
if s.directory? then
begin
fs = Dir.children(file, encoding: enc)
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
raise unless ignore_error
next
end
fs.sort!
fs.reverse_each {|f|
f = File.join(file, f)
ps.unshift f.untaint
}
end
end
end
end
nil
end #
# Skips the current file or directory, restarting the loop with the next
# entry. If the current file is a directory, that directory will not be
# recursively entered. Meaningful only within the block associated with
# Find::find.
#
# See the +Find+ module documentation for an example.
#
def prune
throw :prune
end module_function :find, :prune
end
转换为 PDF
knitr 的一个优点是相同的输入可以转换成许多不同的输出。一种非常有用的格式,当然是 PDF。为了将一个 R markdown 文件转换成 PDF,需要在系统上安装 LaTeX。我们不会在这里解释如何安装 LaTeX,因为网上有大量的文档显示如何进行。
gKnit 附带了一个简单的 LaTeX 样式文件,用于将这个博客作为 PDF 文档进行 gKnit。下面是生成 PDF 格式而不是 HTML 格式的博客的 Yaml 头:
---
title: "gKnit - Ruby and R Knitting with Galaaz in GraalVM"
author: "Rodrigo Botafogo"
tags: [Galaaz, Ruby, R, TruffleRuby, FastR, GraalVM, knitr, gknit]
date: "29 October 2018"
output:
pdf\_document:
includes:
in\_header: ["../../sty/galaaz.sty"]
number\_sections: yes
---
PDF 文档可在以下网址查看:https://www . research gate . net/publication/332766270 _ How _ to _ do _ reproducible _ research _ in _ Ruby _ with _ gKnit
结论
为了进行可重复的研究,需要的主要基本工具之一是一个允许“文化编程”的系统,在该系统中,文本、代码和可能的一组文件可以被编译成一份报告,该报告可以容易地分发给同行。对等体应该能够通过获得完全相同的原始报告,使用相同的文件集来重新运行编译。gKnit 就是这样一个针对 Ruby 和 R 的系统,它使用 R Markdown 来集成文本和代码块,其中代码块可以是 R Markdwon 文件的一部分,也可以从系统中的文件导入。理想情况下,在可重复的研究中,重建报告所需的所有文件应该很容易地打包在一起(在同一个压缩目录中),并分发给同行以便重复执行。
Oracle GraalVM 的承诺之一是,用户/开发人员将能够使用最好的工具来完成手头的任务,而不依赖于编写该工具的编程语言。我们在 GraalVM 和 Truffle interop 消息的基础上开发和实现了 Galaaz,而用 R-gala az 包装 Ruby 或者用 gKnit 包装 Knitr 所花费的时间和精力只是实现原始工具所需时间的一小部分(一个人每天花费几个小时,大约需要六个月)。试图在 Ruby 中重新实现所有的 R 包需要付出与 Python 实现 NumPy、Pandas 和所有支持库同样的努力,这种努力不太可能实现。GraalVM 允许 Ruby“几乎免费”地从这些庞大的库和工具中获利,这些库和工具使 R 成为数据分析和机器学习中最常用的语言之一。
比用 Ruby 包装 R 库更有趣的是,Ruby 为 R 增加了价值,它允许开发人员使用强大的现代结构进行代码重用,而这并不是 R 的强项。正如在这篇博客中所示,R 和 Ruby 可以很容易地进行通信,R 可以以一种极大地扩展其功能和可读性的方式构造在类和模块中。
安装 gKnit
先决条件
- GraalVM (>= rc15)
- 松露红宝石
- FastR
以下 R 包将在必要时自动安装,但如果需要,也可以在使用 gKnit 之前安装:
- ggplot2
- gridExtra
- 针织工
R 包的安装需要一个开发环境,并且很耗时。在 Linux 中,gnu 编译器和工具应该足够了。我不确定苹果电脑需要什么。
准备
- gem 安装 galaaz
使用
- gknit
参考
唐纳德·克努特,1984 年。“识字编程。” *Comput。*第 27 卷第 2 期。英国牛津:牛津大学出版社:97–111。https://doi.org/10.1093/comjnl/27.2.97
威尔金森利兰。2005.图形的语法(统计与计算)。柏林,海德堡:施普林格出版社。