AMT好像在现在用的越来越多了,前段时间知乎上也有这个平台的讨论。

这一个项目也用到了这个平台,从五月份开始接触,直到这两天才正式把整个流程全部完成( 中间隔了一个考试月+实训月,在这里记录下一些心得体会。

Wiki: Obtaining needed services, ideas, or content by soliciting contributions from a large group of people.

首先这是怎样一个项目呢,从AMT这部分来看,是我们有很多组的图像数据,对于每一组,我们需要在AMT上展示出来,并能给予用户分类的操作,然后将用户对于这一组的分类情况保存下来进行分析,如果approve这个worker的结果,那么会pay for it。需求很简单,但是很快就能发现这里面的三个部分,第一个是前端模板,第二个是后台数据,第三个是将模板托管至AMT上,以供worker们工作。

托管

在这里我们使用了别人对于AMT的一个现有框架 simple-amt, 利用它,我们可以通过shell的方式将自己的任务提交到平台上,将更多的经历放在任务本身而不是对平台的熟悉上。

simple-amt is a microframework for working with Amazon’s Mechanical Turk (AMT). It was designed with the following three principles in mind:

  • Abstract away the details of AMT to let you focus on your own task.
  • Place no restrictions on the structure of your AMT tasks.
  • Lightweight and easy to understand.

在需要生成Hits的时候,我们通过运行:

1
2
3
4
5
python launch_hits.py \
--html_template=image_sentence.html \
--hit_properties_file=hit_properties/image_sentence.json \
--input_json_file=examples/image_sentence/example_input.txt \
--hit_ids_file=examples/image_sentence/hit_ids.txt

这个命令也很直观的表明了这个项目的管理方式,image_sentence.html当然就是前端的模板文件,image_sentence.json是已经封装好了的一部分Hit的属性例如duration等,input和id就显而易见了。值得一提的是,这里的前端模板使用了jinja2 作为默认的前端模板,我们也可以利用它来进行各种网页的嵌套。

前端

这部分的需求是将提供一个能让用户去把图片分类的操作,然后想想拖动大概是最方便快捷的方式了,所以左边展示,右边备选区域,利用HTML5原生支持拖动的特点,来完成这个部分。

image

同时关于Html5拖放的操作,可以见此处的简单介绍:HTML5拖放

因此在前端的生命周期,应该是 getData → render → operation → upload result。这里面至少会与服务器产生两次交互。那为了方便起见,我们就直接采用了jsoup跨域请求的方式。

例如getData这个部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function init() {
$.ajax({
async: false,
url: "//207.46.139.xxx:8443/ImageCrowd/image/hit.do",
type: "GET",
data: "",
dataType: "jsonp",
jsonp: 'jsoncallback',
success: function(json) {
render(json);
},
error: function() {
alert("Getting data error.");
}
});
}

另外值得一提的是,为了存储每一次操作后的状态,我们使用了三个数组来表示。在放入的操作中,执行push,在放回的操作中,执行splice。在后来的结果提交上,我们也是将这个数据提交至服务器。

后台

后台和服务器的部分,需要考虑整个逻辑和一些小细节。

数据存储

考虑到一个image可能会存在于不同group的情况,我们将image和group分成两个不同的表,并用一个relation表存储了他们的对应关系。除此之外,还有一个存储了结果和worker id的result表。

另外,所有的图片资源,我们都放在了apache的www目录下面,以保证可以通过外链的方式直接访问到。

后台

我们使用Spring MVC和Hibernate来搭建这个后台,maven进行包的管理。由于逻辑并不是很复杂,整个项目也完成的比较顺利没有遇到大的问题。常用的api有这些

  • /group/new - 增加一个新的group
  • /group/{id} - 根据id来查找这个group的信息,可在type参数中区分gid和mid
  • /group/all - 列出所有group
  • /image/new - 增加一个新的image
  • /image/{id} - 根据id来查找这个image的信息,可在type参数中区分gid和mid
  • /image/hit - 获取一个新的hit数据
  • /result/new - 增加一个新的结果
  • /result/{gid} - 根据gid来查找这个group所有的结果
  • /result/all - 得到所有结果

那我们在前端模板中,就会与/image/hit 和/result/new打交道。既然是jsoup,那我们也应该在这两个接口的返回处使用这样的返回结果:

1
jsoncallback+"("+ json.toString()+")"

除此之外,所有的返回结果都是json格式。另外为了保证每一次getdata不会重复,及每个group只允许做5(customer)次数的分类,因此在hit的逻辑中应该会有根据group的已做次数来进行的下一次推荐。

服务器

我直接用了azure上的一个VM,Ubuntu server14.04.

做到上面所提到的部分,我们至少需要安装好的程序包括:

  • JDK, Tomcat, Apache, Mysql

安装并配置好之后将项目部署上去然后访问,ok有结果,那我们后台就算是搭建成功了。在这个时候我还把自己A0配置的虚拟机狠心升级到了A1,不然好像老是会导致内存不足。

放一张postman测试图:

postman

收尾

然后就到了最后的部分,我们将这几个部分串在一起进行测试,到后来发现了两个问题。

Https站中的Http请求

AMT默认是一个Https站点,但是我们的后台都是默认http请求,在这种情况下,会报一个叫做:

This request has been blocked; the content must be served over HTTPS.

这是我之前没有遇到的情况,因为在google之后,得到了一些有用的信息,我们将请求的url地址改为 //:xxx.xxx.xxx.xxx/….. ,这样会跟随网页的访问方式,然后在服务器这边开启单向的HTTPS,虽然到时候网页会有身份未验证,加载了不安全脚本的提醒,但是貌似只有这样才能保证请求可以访问到,并把图片展示出来。

于是就着手去开启tomcat的Https了,网上教程有很多,java也提供了现成的keytool来生成密钥,很是方便,因此在开启之后尝试访问,ok,成功得到了数据。要注意的是现在”HTTP/1.1“改成了一个包的地址,在server.xml中的这部分总体来看是这样的:

1
2
3
4
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="/home/azureuser/.keystore" keystorePass="xxxx"/>

打错符号之后py莫名其妙的报错

在我用VSC修改了template中的一小段代码后,在launch_hit的时候居然报了一个这样的错误:

error

根据这个错误来看应该是python文件里面的编码问题,舍友之前遇到的时候是通过加了一个文件解决的,那我是不是也可以这样解决呢?尝试之后失败了,于是我又重新安装了一遍python2.7,关机开机,很疑惑的就是半个小时后之前还是好的,可是突然就这样了。于是折腾半个小时还没有任何办法,正准备去洗澡的时候想到了会不会是template的问题,于是裸着上衣来找出bug的所在地。

在最后终于发现了,input的前标签,后面却跟了一个button的后标签。(可是这个报错和这个明明一点关系都没有!

Https后来用了let’s Encrypt, 很好用也很简单,然后在apache那边做了一些简单的转发,RESTful的逻辑更加清楚了。数据也跑了好多轮(

结束

这个工程栈还是蛮全的,不过都很简单。AMT使用起来也确实很方便。