ReactのListとKey

ReactでList(ObjectのArray)を描画する際、mapを利用して各Objectの項目をセットする。

var data = [
        {id: 1, author: "Pete Hunt", text: "This is one comment"},
        {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];

var CommentList = React.createClass({
  render: function() {
    var commentNodes = this.props.data.map(function (comment) {
      return (
        <div author={comment.author}>
          {comment.text}
        </div>
      );
    });
    return (
      <div className="commentList">
        {commentNodes}
      </div>
    );
  }
});

 

もし描画する際に、各ObjectにユニークなKeyを持たせていない場合は警告が出る。

react.js:20541 Warning: Each child in an array or iterator should have a unique "key" prop.

 

以下のように、keyに何かしらのユニークな値を持たせると、以下の警告は消える。

<div author={comment.author} key={comment.id}>

参考文献

React + Flask + Python + MongoDBで作るRSSリーダー

これまで2回に渡ってReactについて学んできた。

testpy.hatenablog.com testpy.hatenablog.com

僕は普段、Pythonを使って機械学習やデータ解析のコードを実装してるのだが、 Webアプリ化したいな、できればReactで実現できたらいいな、と思うことが度々あった。 そこでPythonistaのために、Reactを使った簡単なWebアプリ作成記事があればと、 今(2016/10/31現在)は亡きReactチュートリアル日本語版 をベースに、MongoDBに格納したRSSPythonで読み込み、Flask経由でクライアントに送り、 Reactで描画してみたので、コードを載せておく。 ただし説明はほとんどないので、バリバリのReact初心者の方は、上の記事でベースを固めてから読んでみて下さい。 ちなみに、Reactチュートリアルのソースはまだあります

見た目

こんな感じにする。

f:id:Shoto:20161030013827p:plain

ファイル構造

react-tutorial
├── gigazine_rss.py  // GIGAZINEのRSSをMongoDBに格納
├── node_modules
├── package.json
├── public
│   ├── css
│   │   └── base.css  // RSSを見やすいように加工
│   ├── index.html
│   └── scripts
│       └── example.js  // シンプルなDOMをReactで作成
└── server.py  // Flaskを用いてクライアントとサーバーを連携

データフロー

  1. index.htmlにアクセスする
  2. index.htmlがexample.jsを呼び出す
  3. example.jsがserver.jsを呼び出す
  4. server.jsがgigazine_rss.pyを呼び出す
  5. gigazine_rss.pyがGIGAZINE RSSを取得してMongoDBに格納する
  6. server.jsがgigazine_rss.py経由でMongoDBに格納したRSSを読み込む
  7. server.jsがexample.jsにRSSを渡す
  8. example.jsがRSSのDOMを作成する
  9. DOMがindex.htmlに描画される
index.html
  |
example.js
  |
server.py
  |
gigazine_rss.py <-> GIGAZINE RSS
  |
MongoDB

index.html

Reactはここではなく、exmaple.jsに記述する。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>React Tutorial</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/base.css" />
    <script src="https://unpkg.com/react@15.3.0/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15.3.0/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
    <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/babel" src="scripts/example.js"></script>
  </body>
</html>

example.js

チュートリアルでは、CommentBox、CommentList、Commetがあったが、 ここではCommentをRssに置き換えている。 またBoxとListで十分表現可能で、公式のDocs にも、そのように書けと書いてあるので、RssBoxとRssListのみとした。

var RssBox = React.createClass({
  loadRssFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadRssFromServer();
    setInterval(this.loadRssFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="rssBox">
        <h1 className="siteTitle">GIGAZINE RSS</h1>
        <RssList data={this.state.data} />
      </div>
    );
  }
});

var RssList = React.createClass({
  render: function() {
    var rssNodes = this.props.data.map(function (rss) {
      return (
        <div className="rss" key={rss.id}>
          <h3 className="title">
            <a href={rss.link}>
              {rss.title}
            </a>
          </h3>
          <p className="updated">{rss.updated}</p>
          <p className="summary">{rss.summary}</p>
        </div>
      );
    });
    return (
      <div className="rssList">
        {rssNodes}
      </div>
    );
  }
});

ReactDOM.render(
  <RssBox url="/api/rss" pollInterval={2000000} />,
  document.getElementById('content')
);

server.py

gigazine_rss.pyを呼び出して、GIAZINE RSSの保存と読み込みを行っている。

import json
import os
import time
from flask import Flask, Response, request

from gigazine_rss import Gigazine_RSS

app = Flask(__name__, static_url_path='', static_folder='public')
app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html'))  # client and server side with MongoDB

@app.route('/api/rss', methods=['GET', 'POST'])
def comments_handler():
    Gigazine_RSS().save()
    rss = Gigazine_RSS().read()

    return Response(
        json.dumps(rss),
        mimetype='application/json',
        headers={
            'Cache-Control': 'no-cache',
            'Access-Control-Allow-Origin': '*'
        }
    )


if __name__ == '__main__':
    app.run(port=int(os.environ.get("PORT", 3000)), debug=True)

gigazine_rss.py

testpy.hatenablog.com

この記事を1ファイルで実行できるようにした。

# -*- coding: utf-8 -*-
import sys
import json
import nltk
import numpy
import feedparser
import urllib2
from bs4 import BeautifulSoup
import re
import pymongo

reload(sys)
sys.setdefaultencoding('utf-8')

DATABASE_NAME = 'gigazine'
COLLECTION_NAME = 'rss'

class Gigazine_RSS:
    def __init__(self):
        pass


    def save(self):
        rss = self.__get_rss()
        self.__save_rss(rss)

        return rss


    def __get_rss(self):
        rss_url = 'http://feed.rssad.jp/rss/gigazine/rss_2.0'
        articles = feedparser.parse(rss_url)

        rss = []
        for e in articles.entries:
            dic = {
                'id': e.id,
                'updated': e.updated,
                'title': e.title,
                'link': e.link,
                'summary': self.__getTextOnly(BeautifulSoup(e.summary))
            }
            rss.append(dic)

        return rss


    # Extract the text from an HTML page (no tags)
    def __getTextOnly(self, soup):
        v = soup.string  # Split by tags and check whether nested tags
        if v == None:  # If tags are nested
            c = soup.contents  # Eliminate outmost tags
            resulttext = ''
            for t in c:
                subtext = self.__getTextOnly(t)
                # If the subtext is null(u''), don't append it
                if len(subtext) > 0:
                    resulttext += subtext + '\n'
            return resulttext
        else:
            return v.strip()  # Eliminate '\n'


    def __save_rss(self, data):
        client = pymongo.MongoClient('localhost', 27017)
        db = client[DATABASE_NAME]
        co = db[COLLECTION_NAME]
        co.drop()

        co.insert(data)


    def read(self):
        client = pymongo.MongoClient('localhost', 27017)
        db = client[DATABASE_NAME]
        co = db[COLLECTION_NAME]

        data = [d for d in co.find()]

        rss = []
        for c in co.find():
            c.pop('_id', None)
            rss.append(c)

        return rss


if __name__ == '__main__':
    Gigazine_RSS().save()
    #Gigazine_RSS().read()

base.css

見やすいようにセンタリングやコントラストなどの調整をした。

body {
  background: #fff;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.7;
  margin: 0;
  padding: 30px;
}

a {
  color: #4183c4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

code {
  background-color: #f8f8f8;
  border: 1px solid #ddd;
  border-radius: 3px;
  font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
  font-size: 12px;
  margin: 0 2px;
  padding: 0 5px;
}

h1, h2, h3, h4 {
  font-weight: bold;
  margin: 0 0 15px;
  padding: 0;
}

h1 {
  font-size: 2.5em;
}

h2 {
  border-bottom: 1px solid #eee;
  font-size: 2em;
}

h3 {
  font-size: 1.5em;
}

h4 {
  font-size: 1.2em;
}

p, ul {
  margin: 15px 0;
}

ul {
  padding-left: 30px;
}

.rssBox {
  width: 600px;
  margin: auto;
}

.updated {
  color: #999;
}

.siteTitle {
  text-align: center;
  margin: 20px 0 40px;
}

.summary {
  margin: 0 0 40px;
}

Reactチュートリアル入門: Pythonサーバーサイド連携編

チュートリアル | React がなくなったようで。。良いチュートリアルだったのに。。
まあとりあえず、前回は クライアントサイドのみでWebサイトを表示したので、 今回はサーバーサイドで取得したデータをクライアントサイドに渡して表示させる。 とは言っても、JSONファイルを表示するだけなので、非常に簡単。 ただし、サーバーサイドはNode.jsではなく、Pythonを使う。 その前にまず、前回のReactコードを少しカスタマイズする。

クライアントサイドのカスタマイズ

CommentBox

前回、シンプルだったCommentBoxに色々追記する。

  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },

Ajax通信を行う。urlは、後ほどReactDOM.renderでCommentBoxに渡される。 これがサーバーサイドへのパスになる。dataはサーバーサイドから返ってきたデータ。  

  getInitialState: function() {
    return {data: []};
  },

クライアントサイドのdataの初期化。dataはObjectのArrayなので[]となる。  

  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },

DOMに関わる初期化。 loadCommentsFromServerで設定したAjaxリクエストや、リフレッシュ頻度を指定するsetIntervalの登録など、 server-side rendering時には必要ない初期化処理についてはこの中で行う。

以上より、今回のCommentBoxは次のようになる。  

var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList comments={this.state.data} />
      </div>
    );
  }
});

ReactDOM.render

設定したCommentBoxに、ReactDOM.renderでパラメーターを渡す。 サーバーサイドのパスとなるurlと、リフレッシュ頻度となるpollIntervalを指定する。 前回はurlキーではなく、dataキーにクライアントサイドで定義したdataを渡していた。 pollIntervalは公式では2000(2秒)だが、短すぎるので2000秒としておく。

ReactDOM.render(
  <CommentBox url="/api/comments" pollInterval={2000000} />,
  document.getElementById('content')
);

Pythonサーバーサイド

Pythonのサーバーサイドは、Flaskを使っている。 Flaskは僕も知らないので、ググって下さい。 でもFlaskはまったく悩むことなく簡単に使えるようで、 クライアントサイドで指定したurl='/api/comments' [check1!] のすぐ下のcomments_handler() の中でデータを取得して返している。 今回はGETのみなので、単純にserver.pyと同じディレクトリーに置かれたcommnet.jsonを読み込んで [check2!] 、 json形式にPythonで適切に変形して、クライアントサイドに返している [check3!] ことが分かる。

import json
import os
import time
from flask import Flask, Response, request

app = Flask(__name__, static_url_path='', static_folder='public')
app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html'))  # client and server side with json file

@app.route('/api/comments', methods=['GET', 'POST'])  # Check1!
def comments_handler():
    with open('comments.json', 'r') as f:
        comments = json.loads(f.read())  # Check2!

    if request.method == 'POST':
        new_comment = request.form.to_dict()
        new_comment['id'] = int(time.time() * 1000)
        comments.append(new_comment)

        with open('comments.json', 'w') as f:
            f.write(json.dumps(comments, indent=4, separators=(',', ': ')))

    return Response(
        json.dumps(comments),  # Check3!
        mimetype='application/json',
        headers={
            'Cache-Control': 'no-cache',
            'Access-Control-Allow-Origin': '*'
        }
    )

if __name__ == '__main__':
    app.run(port=int(os.environ.get("PORT", 3000)), debug=True)

実行

$ python server.py

上記のコマンドを実行すると、以下のように表示されるはず。

f:id:Shoto:20161030002637p:plain

Reactチュートリアル入門:クライアントサイド編

チュートリアル | React(何故か2016-10-23時点ではNot Found) を理解しながら写経すると、Reactで簡単なWebアプリが作れるようになる。 しかし、僕自身もそうだが、テンプレートを使ったWebアプリしか作ったことしかなく、 Reactを理解したいという人向けに、今回はクライアントサイドだけをいじってWebアプリを作成する。 そもそもReactとは何か、Reactを使うと何が便利なのかを知りたい人は Reactを使うとなぜjQueryが要らなくなるのか が分りやすい。

Reactチュートリアルの始め方

Reactチュートリアルのプロジェクトをクローンして、パッケージをインストールする。

$ git clone https://github.com/reactjs/react-tutorial.git
$ cd react-tutorial
$ npm install

初期設定

プロジェクトの中身を見ると、色々ファイルが入っているので、必要なものだけに絞って残りは削除する。

react-tutorial
├── LICENSE
├── README.md
├── app.json
├── comments.json
├── db.py
├── db.pyc
├── node_modules
│   ├── body-parser
│   └── express
├── npm-debug.log
├── package.json
├── public
│   ├── css
│   ├── index.html
│   └── scripts
├── requirements.txt
├── server.go
├── server.js
├── server.php
├── server.pl
├── server.py
└── server.rb

 
以下が削除した結果。

react-tutorial
├── comments.json     // 今回は使わない
├── node_modules      // npm installで生成されたフォルダー
│   ├── body-parser
│   └── express
├── package.json      // メタ情報ファイル
├── public
│   ├── css
│   ├── index.html    // 今回編集する唯一のファイル
│   └── scripts
├── requirements.txt  // 後で使う
├── server.js         // 後で実行する
└── server.py         // 後で実行する
  • comments.json
    • コメントが書かれたJSONファイル。今回は使わない。
  • node_modules
    • "npm install"を実行した時に生成されたフォルダー。Reactなど今回使うnode.js関連のパッケージが入っている。基本触らない。
  • package.json
    • ライセンスや著者、バージョンなど記述されたメタ情報ファイル。"npm install"の後でパッケージの依存関係の記述も自動的に追加されている。
  • public
    • HTML・CSSJavaScript・画像など、クライアントサイドのファイルを置く場所。
  • index.html
    • 今回は編集する唯一のファイル。
  • server.jsとserver.py
    • アプリケーション・サーバーを起動するNode.jsとPythonのコード。他の言語を使いたい人は、それらのファイルを残す。
  • requirements.txt
    • Pythonのサーバーサイドを実行する際に必要なライブラリーをインストールするための設定ファイル。

index.htmlを開くと以下のようになるが、今回は下の方のscriptダグで囲まれたエリアにReactコードを追記していく。 そのため、"script/example.js"をインポートしているscriptタグをコメントアウトしておく。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>React Tutorial</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/base.css" />
    <script src="https://unpkg.com/react@15.3.0/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15.3.0/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
    <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <!--
    <script type="text/babel" src="scripts/example.js"></script>
    -->
    <script type="text/babel">
    // To get started with this tutorial running your own code, simply remove
    // the script tag loading scripts/example.js and start writing code here.
    </script>
  </body>
</html>

Reactの実装

最終的なWebアプリの見た目はこんな感じ。

f:id:Shoto:20161023190302p:plain

 
まずは、Webアプリで表示するためのJSONを記述する。

      var data = [
        {id: 1, author: "Pete Hunt", text: "This is one comment"},
        {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
      ];

 
ここからReact。 上記ので記述したdataを、CommentBoxに入れた結果を、index.htmlの<div id="content"></div>内にレンダリングする、 という意味のコードを記述する。 レンダリングするDOMの構造を、このCommentBoxから始まるReactによって作成することが、今回の主な内容であり、Reactのコアな部分になる。

      ReactDOM.render(
        <CommentBox data={data} />,
        document.getElementById('content')
      );

 
上記のdataキーは、CommentBoxでは{this.props.data}として表される。 逆に言えば、親のデータは{this.props.xxx}で取得できる。 また{this.props.data}は、子であるCommentListcommentsキーにそのまま渡される(オリジナルはdataキー)。 ここより下は、コメントのリストが表示されるので、h1タグでCommentsと記述しておく。

      var CommentBox = React.createClass({
        render: function() {
          return (
            <div className="commentBox">
              <h1>Comments</h1>
              <CommentList comments={this.props.data} />
            </div>
          );
        }
      });

 
上記のcommentsキーは、CommentListでは{this.props.comments}となる。 これはJSONのobjectのarrayなので、個々のobjectを子であるCommentに渡すために、 map関数を使っている。またCommentへはobjectの値をセットしている。

      var CommentList = React.createClass({
        render: function() {
          var commentNodes = this.props.comments.map(function (comment) {
            return (
              <Comment author={comment.author} key={comment.id}>
                {comment.text}
              </Comment>
            );
          });
          return (
            <div className="commentList">
              {commentNodes}
            </div>
          );
        }
      });

 
上記のauthorキーは{this.props.author}になる。 また上記の{comment.text}{this.props.children}になる。

      var Comment = React.createClass({
        render: function() {
          return (
            <div className="comment">
              <h2>
                {this.props.author}
              </h2>
              {this.props.children}
            </div>
          );
        }
      });

 
このようにReactによって、CommetBox以下でDOMの構造が構築された後、最終的にレンダリングされる。 index.htmlに追記したコードをまとめると以下のようになる。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>React Tutorial</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/base.css" />
    <script src="https://unpkg.com/react@15.3.0/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15.3.0/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
    <script src="https://unpkg.com/remarkable@1.7.1/dist/remarkable.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <!--
    <script type="text/babel" src="scripts/example.js"></script>
    -->
    <script type="text/babel">
      // To get started with this tutorial running your own code, simply remove
      // the script tag loading scripts/example.js and start writing code here.
      var data = [
        {id: 1, author: "Pete Hunt", text: "This is one comment"},
        {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
      ];

      var CommentBox = React.createClass({
        render: function() {
          return (
            <div className="commentBox">
              <h1>Comments</h1>
              <CommentList comments={this.props.data} />
            </div>
          );
        }
      });

      var CommentList = React.createClass({
        render: function() {
          var commentNodes = this.comments.data.map(function (comment) {
            return (
              <Comment author={comment.author} key={comment.id}>
                {comment.text}
              </Comment>
            );
          });
          return (
            <div className="commentList">
              {commentNodes}
            </div>
          );
        }
      });

      var Comment = React.createClass({
        render: function() {
          return (
            <div className="comment">
              <h2>
                {this.props.author}
              </h2>
              {this.props.children}
            </div>
          );
        }
      });

      ReactDOM.render(
        <CommentBox data={data} />,
        document.getElementById('content')
      );
    </script>
  </body>
</html>

アプリケーション・サーバーの実行

以下のいずれかを実行し、http://localhost:3000/にアクセスすれば、 2つのコメントがレンダリングされた画面が表示されるはず。

  • Node.js
node server.js
pip install -r requirements.txt
python server.py

pipの実行は初回だけでよい。

参考文献

Atomがフリーズしたときの対処法

macOS Sierraにアップデートしたら、"Editor is not responding"となったので、その対処法をメモに残しておく。

手順

  1. Finderのメニューバーから、[移動] > [フォルダーへ移動...]と選択
  2. テキストボックスに<~/.atom/storage>を入力して[移動]ボタンをクリック
  3. フォルダー内のファイルを削除

なお、Atom関連の設定が色々と飛ぶようなので、自己責任で。 僕の場合、ファイルは"application.json"しかなかった。 削除したら、"Editor is not responding"というメッセージが表示されることもなくなったし、 インストールしたパッケージやテーマも飛ばなかった。

参考文献

Python製プログレスバーを3行で書く

for文の中に標準出力を書くと、ターミナルが文字で埋め尽くされるので回避したいが、 for文の中身が重くて無反応になるのも困る。 なのでプログレスバーを書けば良い、という結論に至るわけだが、 さくっと色んなfor文に書きたいので、3行で書く方法を説明する。

インストール

progressbarというそのままな名前のライブラリーをインストールする。 2016年10月現在は、progressbar2 3.10.1という謎のバージョン。

# pip install progressbar2

実装

手順は次の3つ。

  1. インポート
  2. 初期化
  3. 更新

for文の外でインポートと初期化を行い、for文の中で更新を行う。 初期化の時に、繰り返し回数をパラメーターに指定する。 またfor文の中では、個々のデータではなく、全てのデータをindex(i)で指定して扱えるようにする。 なおサンプルとして、RSSを入れるとページのソースを取得してくれるメソッドget_src()を利用する。 実装は次の通り。

from progressbar import ProgressBar  # 1. インポート

def main(rss):
    pb = ProgressBar(maxval=len(rss)).start()  # 2. 初期化
    for i in range(len(rss)):
        get_src(rss[i])
        pb.update(i+1)  # 3. 更新

結果

ターミナルの幅をフルで使った出力になる。 カスタマイズしたい場合は、参考文献を参照。

f:id:Shoto:20161009165725g:plain

参考文献

Luhnの要約アルゴリズムの後処理

これまでLuhnの要約アルゴリズム前処理として基本的な自然言語処理を、 本処理としてテキストの各文のスコア付けを行った。 今回は後処理として要約テキストとして表示させる。 要約テキストは、スコア付けされた文を選択することで生成される。 ここでは2種類の選択方法について説明する。

各文のスコア

各文のスコアは以下のようになる。

index score sentence
0 5.1 スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました
1 6.8 AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました
2 2.3 Google翻訳ではこれまでフレーズベースで機械翻訳するPBMTというシステムが採用されていましたが、このような単語やフレーズごとに機械的に文章を翻訳する方法ではなく、文章全体をひとつの翻訳単位として捉えることができるのがGNMTだそうです
3 2.3 Googleの研究者によると、「GNMTの『文章全体をひとつの翻訳単位として捉える』アプローチの長所は、工学的設計の選択肢がPBMTよりも少なくて済むことです」とのこと
4 1.3 実際にGNMTを駆使した最初の翻訳では、既存のGoogle翻訳と遜色ない翻訳精度がみられたそうです
5 1.8 さらに、何度も翻訳を重ねることで、GNMTは優れた翻訳と素早い翻訳スピードの両立が可能になっている模様
6 4.5 Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと
7 2.7 GoogleによるとGNMTは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと
8 3.2 以下のグラフは人間・GNMT・PBMTの3つによる翻訳を6段階評価して比較したもので、最も翻訳精度が高いのは人間による翻訳ですが、フランス語から英語に翻訳する場合や英語からスペイン語に翻訳する場合、人間とGNMTの間にそれほど大きな差は存在しないそうです

標準偏差による文の選択

0.5σ以上のスコアを持つ文を選択する。

# 要約アプローチ1
# 平均スコアとフィルタとしての標準偏差を使って重要ではない文を取り除く
avg = numpy.mean([s[1] for s in scored_sents])
std = numpy.std([s[1] for s in scored_sents])
mean_scored = [(sent_idx, score) \
               for (sent_idx, score) in scored_sents \
               if score > avg + 0.5 * std]

平均 3.3, 標準偏差1.7なので、0.5σは4.2となる。 そのため0, 1, 6番目の文が選択される。

>>> print mean_scored
[(0, 5.142857142857143), (1, 6.75), (6, 4.5)]

スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました。AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました。Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと。

TopNの文の選択

単純に指定された数の文だけスコアが高い順に選択する。

self.TOP_SENTENCES = 5  # 「トップN」要約のために返す文の数

# 要約アプローチ2
# トップNにランクされた文だけを返す
top_n_scored = sorted(scored_sents, key=lambda s:s[1])[-self.TOP_SENTENCES:]
top_n_scored = sorted(top_n_scored, key=lambda s:s[0])

そのため、0, 1, 6, 7, 8番目の文が選択される。

>>> print top_n_scored
[(0, 5.142857142857143), (1, 6.75), (6, 4.5), (7, 2.6666666666666665), (8, 3.2)]

スマートフォンでもPCでもオフラインでも使え、100以上の言語に対応しているGoogleの翻訳サービス「Google翻訳」が、ちょうど10周年を迎えるタイミングでより自然な翻訳を可能にする人工知能(AI)を活用した「GNMT」システムを発表しました。AI研究で複数の大手IT企業とのパートナーシップを結んだばかりのGoogleは、AIを駆使した翻訳システム「GNMT(Google Neural Machine Translation)」を発表しました。Googleによると、GNMTを用いることでGoogle翻訳は翻訳ミスを55~85%も軽減できるようになるとのこと。GoogleによるとGNMTは一部のケースでは人間レベルの翻訳が可能なレベルに達しているとのこと。以下のグラフは人間・GNMT・PBMTの3つによる翻訳を6段階評価して比較したもので、最も翻訳精度が高いのは人間による翻訳ですが、フランス語から英語に翻訳する場合や英語からスペイン語に翻訳する場合、人間とGNMTの間にそれほど大きな差は存在しないそうです。