Doraemonls

用机器学习加速你的网站

Doraemonls · 2017-04-14翻译 · 882阅读 原文链接

我一生中大约73%的时间都在思考网络性能:如何在慢速手机上能播放60FPS的画面,用完美的顺序加载资源,通过离线缓存能做的一切。等等等等。

但最近我一直在想,我对Web性能定义是否太狭隘了。从用户的角度来看,所有的炫酷事情都只是性能的一小部分。

所以我打开了一个非常熟悉的网站,并把用户需要做的任务都过了一遍,而且还给每个任务定了时。 (我们需要一个定制用户旅程的时间线工具)

很快我就发现有一些可以改进的步骤了。

本文的其余部分重点介绍一个特定网站上的特定步骤。但是我想这一解决方案(也就是机器学习)可以在许多不同的站点中与许多不同场景有相似的应用。

拨开问题的云雾

这一例子网站是用户可以卖二手物品,其他用户可以淘宝的地方。

当用户在网站上发帖出售东西时,他们要先选择物品的类别,选择期望的广告包,填好细节内容,然后预览广告内容,最后发布这个广告。

第一步:选择一个类别,就把我带坑里了。

首先, 这里一共有674个类别。我真不知道我的破皮艇到底是哪一类。Steve Krug说的很好:别让我思考

其次,即便我清楚我的东西在哪个类,在哪个子类,在哪个子类的子类,这个过程也要起码花上12秒。

如果我给你说,你的页面加载时间可以减少12秒,你肯定会想怎么做。那为什么不想想怎么在其他地方减少12秒呢?

就像 Julius Caesar说的:“12秒能做多少事!”。

还有,对用户来说,无知即幸福。我认为如果我把标题、描述、商品价格输入一个训练好的机器学习的模型里,它就能自动的识别这个东西是什么类别的。

因此,相比让用户花了那么多时间来选择分类,那不如让他们在reddit上找一个手工床上多花12秒。

机器学习 -- 不要逃避,拥抱它

When this began, I knew absolutely nothing about machine learning, other than it could play video games, and outperform the world’s best go-go dancer at chess. 起初,我完全不知道什么是机器学习,只知道它能打打游戏,最近还甚至打败世界顶级的下棋选手。

所以我就尝试学习一下。下面的步骤花了我不到一小时:

  1. Google "Machine Learning"

  2. 点开了很多页面

  3. 发现了Amazon的机器学习

  4. 意识到我可以不需要了解机器学习

  5. 松了一口气

(注意:既然我没有认真的学习过机器学习,那我这整篇文章的措辞都可能是用错了的。)

整个过程,是黑箱的

亚马逊 在他们的ML文档里 搞定了这件事。如果你觉得智商被我的文章侮辱了,那么你可以先把这篇文章放一边,过5个小时再来读读看。我可不会尝试在这里总结有关机器学习的内容。

读完文档,我计划如下:

  • 在一个CSV文件里放一些数据。每一行代表一件物品,比如我的皮艇。列就是标题、目录、价格、分类。

  • 把CSV上传到ASW的S3 bucket里去。

  • 用这个数据‘train’机器理解这个数据(这在他们的界面上都做好了,还有在线帮助)。他们的云机器人就能知道如何根据标题、描述和价格预测分类了。

  • 然后在加入一些代码,把用户输入发给AWS的接入点(Amazon会自动创建接入点),预测分类就在界面上显示了。

实例网站

我放了一个大大的表单来模拟用户的输入输出。

现在你就能看到一些感兴趣的内容了。相信我,这些分类都是从这个机器学习的模型里自动计算出的。

Let’s try selling a fridge: 现在让我们来卖个冰箱 duang~

鱼缸呢?

看来这个小可爱知道鱼缸是什么!

必须承认,看到这个我还是挺开心的。你看,这还挺赞的,不是么?

如果你很好奇我是怎么做这个表单的,,我用到了React、Redux、jQuery、Mobx、BlueBird、Bootstrap、Sass、Compass、NodeJS、Express、Lodash,另外再用Webpack打个包。所有文件加起来不超过1M,


好了,现在我们看看次要点的部分。

因为我只是随便试试,我需要从某些地方拿到足够多的数据。我需要至少十几个类别的10000多个物品。我找了一个当地的交易网站,看了下他们的URL和DOM,然后写了个脚本抓了数据,并且输出到一个CSV中去。这件事大概花了我4个多小时,一半的时间花在调通这个脚本上。

我把得到的CSV上传到S3上去,然后按着教程又建了新的模型,再训练。总共消耗的CPU时间是3分钟。

界面上有一个很好的实时预测的部分,所以我可以测试。如果我设定某些参数,它就会显示预测结果

很好,正常工作。High Five。

现在,我并不想用Amazon API,因为我不希望这个API是公开的。所以,我需要用我自己的node server提供这个API。

后端代码

方法很简单。我只需要传入model ID和一些数据,然后它返回预测结果。

 const AWS = require('aws-sdk'); 
 const machineLearning = new AWS.MachineLearning(); 
 const params = { 
     MLModelId: 'some-model-id', 
     PredictEndpoint: 'some-endpoint', 
     Record: {}, 
 }; 
 machineLearning.predict(params, (err, prediction) => { 
     // we have a prediction! 
 });

中途我差点放弃了,因为大小写变化太多,我很难集中注意力。不过还要坚持战斗。

这个纪录,哦,抱歉,我是说这条记录,是一个JSON对象,它的属性就是用来训练模型的那些属性(标题,描述和价格)。

我可不想成为那些发很少代码对大家没帮助的人,所以,下面是我所有的 server.js ,以及/predict的入口。

 const express = require('express');
 const bodyParser = require('body-parser');
 const AWS = require('aws-sdk');
 const app = express();
 app.use(express.static('public'));
 app.use(bodyParser.json());
 AWS.config.loadFromPath('./private/aws-credentials.json');
 const machineLearning = new AWS.MachineLearning();
 app.post('/predict', (req, res) => {
   const params = {
     MLModelId: 'my-model-id',
     PredictEndpoint: 'https://realtime.machinelearning.us-east-1.amazonaws.com',
     Record: req.body,
   };
   machineLearning.predict(params, (err, data) => {
     if (err) {
       console.log(err);
     } else {
       res.json({ category: data.Prediction.predictedLabel });
     }
   });
 });
 app.listen(8080);

你还记得不能用JavaScript写后端代码的日子么?

还有aws-credentials.json的部分:

{
  "accessKeyId": "my-access-key-id",
  "secretAccessKey": "shhh-secret-squirrel",
  "region": "us-east-1"
}

对了,我的/private目录在.gitignore里,所以不会上传

恩,后端代码就完成了。

前端代码

表单后的代码部分其实很简单,就做这么几件事:

  • 监听相关字段的blur事件

  • 获取元素的具体值

  • 把数据 POST到我刚创建的 /predict入口

  • 把返回的结果填到category字段,

(function() {
  const titleEl = document.getElementById('title-input');
  const descriptionEl = document.getElementById('desc-input');
  const priceEl = document.getElementById('price-input');
  const catSuggestionsEl = document.getElementById('cat-suggestions');
  const catSuggestionEl = document.getElementById('suggested-category');

  function predictCategory() {
    const fetchOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        title: titleEl.value,
        description: descriptionEl.value,
        price: priceEl.value,
      })
    };

    fetch('/predict', fetchOptions)
      .then(response => response.json())
      .then(prediction => {
        catSuggestionEl.textContent = prediction.category;
        catSuggestionsEl.style.display = 'block';
      });
  }

  document.querySelectorAll('.user-input').forEach(el => {
    el.addEventListener('blur', predictCategory);
  });
})();

还记得不能对NodeListforEach的日子么?

完成。这就是全部的代码。功能就是把用户输入的数据作为输入,调用一个机器学习的机器人对物品进行预测,最后输出物品的类别。

安静的把我的钱带走

先别着急走,朋友。这好魔法也不是免费的...

用来训练的上述模型(大约10,000行/ 4列的CSV)是6.3 MB。建一个server接受请求,又要花掉6.3M的内存。成本大概是每小时$0.0001,大约一年8刀。我买手套的钱都比这多。

每条预测结果也收费,大概是$0.0001。所以,你懂的,别做太随机的预测。

当然了,不只是Amazon提供这些好货,我只是没从别人那找到更便宜的。

Google有 TensorFlow,但是他们的getting started guide文档里我就看不下去。

Microsoft也有一个Machine Learning offering,不过我还在为IE6生气(不过,或许以后我会写一篇Amazon VS Microsoft的博文)。

简单总结

也许我很容易惊讶(我还记得当我意识到'news'是'new'的复数形式的情景),但我还是认为这是非常了不起的事。它允许像你这样的普通人(在某种程度上来说,比如我也是)能够在操作机器学习,并可能对用户体验进行一些很大的改进。

下一步呢?

上面这个例子肯定是我设计出来的,我承认。我还留了一张把烤面包机分类成马的截图。

可以列出所有我能预见的问题,但如果你能自己发现这些问题,会更有意思。

所以,你自己去试试把。如果你有什么进展,记得回复,我很乐意在评论里看到。

相关文章