文章の異常箇所を検知するLSTMを利用したAPIをDjangoで実装

スポンサードリンク



こんにちは。sinyです。

今回はリクルートテクノロジーズが公開しているA3RTのproofreadingというAPIを使ったAIデモアプリをDjangoで実装してみました。

 

Proofreading APIとは?

以下、概要を引用します。

Proofreading APIはLSTMを利用して文章として怪しい箇所を検知するAPIです。
例えば、"経験や資格や活かせる職場です"という文章に対して"経験や資格<<<や>>活かせる職場です"という形で不自然な箇所を指摘し、その怪しさ度を返します。また、文章として書き換えた方が良さそうな単語も検知をします。

 

まずはどんな感じのデモアプリが出来上がるかこちらをどうぞ。

API KYEの取得

まず、こちらのページからAPIキーを発行します。

リンク先ページに飛んだら以下のボタンからAPIキーを取得しましょう。

APIの使い方

まず最初にproofreadingの使い方を簡単に説明します。

以下のエンドポイントに解析したい文章情報をHTTPSリクエストとして投げると、文章としておかしい部分を判定して結果を返してくれます。

エンドポイントhttps://api.a3rt.recruit-tech.co.jp/proofreading/v2/typo

メソッドはメソッドはGET/POSTの2つが対応しています。

リクエスト時に以下のパラメータが必須になっています。

必須のパラメータ
  • apikey発行したキー
  • sentence:チェック対象となる文字列情報

いくつか任意のパラメータがありますが、今回は以下のパラメータを利用してみます。

任意のパラメータ
  • sensitivity:チェックの感度(low/medium/highの3つ)
    ※未指定時はmedium

とりあえずどんな感じの動きになるか、コマンドベースで確認してみます。

私の得意分野は画像処利と自然言語処理です。」という文字列をAPIで投げて戻り値を確認してみます。

「画像処」の部分が明らかに誤った文章なので、この「」の部分を誤りと判定してくれることを期待します。

以下のPythonコードでリクエストを投げて結果を取得してみます。

import urllib.request
import urllib.parse
import json
import requests

API = "https://api.a3rt.recruit-tech.co.jp/proofreading/v2/typo"
KEY ="<APIキーを指定>"

quoted_text ="私の得意分野は画像処利と自然言語処理です。"


values = {
'apikey': KEY,
'sentence':quoted_text,
'sensitivity':"medium",
}
# パラメータをURLエンコードする
params = urllib.parse.urlencode(values)
# リクエスト用のURLを生成
url = API + "?" + params

#リクエストを投げて結果を取得
r = requests.get(url)
#辞書型に変換
data = json.loads(r.text)

 

実行結果は以下のようになります。

>>> data
{'resultID': '5818bde37bf5', 'status': 1, 'message': 'pointed out', 'inputSentence': 
'私の得意分野は画像処利と自然言語処理です。', 'normalizedSentence': '私の得意分野は画像処利と
自然言語処理です。', 'checkedSentence': '私の得意分野は画像処 <<利>> と自然言語処理です。', 
'alerts': [{'pos': 10, 'word': '利', 'score': 0.8740427199300843, 'suggestions':
 ['理', '分', '方']}]}
>>>

 

いろいろと戻り値がありますが、ポイントとなる値は以下の通りです。

戻り値の概要
  • status :0(文章に異常なし)、1(文章に異常あり) 
  • inputSentence:リクエストで指定した文字列
  • normalizedSentence:チェック用に正規化された文
  • checkedSentence:チェック後の文。指摘箇所を<<>>で示してくれる。
  • alerts:文章の異常個所の情報を格納した配列
    ※alertsの中には以下の情報が格納されている。
    ーpos :指摘箇所。文の先頭からの文字数
    ーword: 指摘文字
    ーscore :指摘した単語の疑わしさを示す指標(0~1) 1に近いほど疑わしい。
    ーsuggestions: 指摘箇所を置き換える候補。より自然な文字から順にlist形式で格納。

 

>>> data['alerts']
[{'pos': 10, 'word': '利', 'score': 0.8740427199300843, 'suggestions': ['理', '分', '方']}]

 

上記の実行結果の例で説明すると、

10文字目の「」が87%の確率で疑わしく、「理→分→方」の順で正しいと思われる単語を提案してくれています。

Proofreading APIを使ったDjangoアプリの実装手順

それでは、Proofreading APIを使ってDjangoアプリを作っていきます。

Djangoの細かい説明は割愛していますので、基礎を理解できていない場合は以下の記事等を参考にしてみてください。

モジュールのインポート

まずは、必要なモジュールをインポートします。

pip install django
pip install django-bootstrap4
pip install django-widgets-improved
pip install requests

 

django-bootstrap4はBootstrapを適用するために、django-widgets-improvedはformのclassにbootstrapのものを適用するために使います。

Djangoのプロジェクトとアプリケーションは適宜作成しておきましょう。
※今回はアプリケーション名をappとします。

settings.pyのカスタマイズ

以下の通りインポートしたモジュールとアプリケーションを追加しておきます。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap4',      #追加
    'widget_tweaks',   #追加
    'app',             #追加
]

 

テンプレートのパスをプロジェクトフォルダの直下に指定します。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],  #変更
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

 

日本語に変更しておきます。

LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'

 

settings.pyのカスタマイズは以上です。

URLパターンの設定

プロジェクト直下のurls.py

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app/', include('app.urls')),   #追加
]

アプリケーション直下のurls.py

from django.urls import path
from . import views

urlpatterns = [
    path('demo/',views.demo,name='demo'),
]

 

フォームの設定(forms.py)

 

from django import forms

CHOICES = (('low', '低',), ('medium', '中',), ('high', '高',))


class UserForm(forms.Form):
     textone = forms.CharField(label='入力文章',max_length=500,
     min_length=1,widget=forms.Textarea(attrs=
     {'id': 'textone','placeholder':'ここにチェックしたい文章を入力してください\n(500文字まで)'}))
     sensitivity = forms.ChoiceField(label='チェックの厳密さ', widget=forms.Select, choices=CHOICES
     )

 

2つのフォームを定義します。

1つ目は、解析する文章を入力するフォームをforms.Textareaを使って定義します。
2つ目は、チェックの感度(低、中、高)をリスト形式で選択できるようにするためforms.ChoiceFieldを使ってフォームを定義します。

 

utils.pyの作成(リクエストを投げる部品)

 

import requests
import json
import re

class Api:
    def __init__(self):
        self.key = '<APIキーをここに指定する>'
        self.api = 'https://api.a3rt.recruit-tech.co.jp/proofreading/v2/typo'

    def get(self,inputtext,sensitivity):
        url = self.api
        quoted_text = inputtext
        r = requests.post(url,{'apikey':self.key,'sentence':quoted_text,'sensitivity':sensitivity})
        data = json.loads(r.text)
        rets = []
        suggestions = []
        if data['status'] == 1:
            rets = '<font color="red">疑わしい部分と判定された箇所あります。</br>ハイライトされた箇所を確認してください。</font></br><hr>'
            text = data['checkedSentence']
            rets = rets + self.__trans_word(text)
            #指摘単語に対するSuggestionを取得    
            for i in range(len(data['alerts'])):
                suggestions.append([data['alerts'][i]['word'], data['alerts'][i]['suggestions']])

        elif data['status'] == 0:
            rets = "この文章に誤字脱字はありません。</br>指摘すべき修正を見つけられませんでした。"
        else:
            rets = "エラーがありました。</br>応答コードは" + data['status'] + "です。"
        return rets, suggestions

    def __trans_word(self,inputtext):
        replacements = {'<<':'<span class="mark font-weight-bold text-danger" style="background-color:yellow">','>>':'</span>'}
        return re.sub('({})'.format('|'.join(map(re.escape, replacements.keys()))), lambda m: replacements[m.group()], inputtext)

 

Apiクラスを定義して2つのメソッドを定義しています。

  • getメソッド
    フォームから解析文章とチェック感度のパラメータを受け取りリクエストを投げ、解析結果を戻します。
  • __trans_wordメソッド
    APIから返ってきた指摘箇所「<<指摘文字>>」の部分をハイライト表示させるためのhtmlタグを設定するメソッド
    指摘文字に以下のようなタグを設定してくれます。

    <span class="mark font-weight-bold text-danger" style="background-color:yellow">指摘文字</span>

 

※上記の__trans_wordメソッドなどは以下の記事がとても参考になりました。
※本記事内でもいくつかのコードを引用させて頂いております。

 

ビューの設定(views.py)

 

from django.http.response import HttpResponse
from django.shortcuts import render, render_to_response
from . import forms
from django.template.context_processors import csrf
from . import utils


message = '''■誤字脱字の定義<br>
        <b>Proofreading APIで指摘されるもの。</b><br>
        漢字変換間違い:読みは正しいが漢字が違う。<br>
        助詞間違い:助詞の用法が違う、または、不要な助詞が入っている。<br>
        文法間違い: 漢字・助詞間違いに該当しないもの。脱字。助詞以外の不自然な文字が挿入されているもの。<br>
        その他、1〜数文字が間違っている文章。<br>
        <b>Proofreading APIで指摘されないもの。</b><br>
        ◆敬語の使い方間違い。<br>
        ◆体言どめを止めたほうがいいケース。<br>
        ◆ひらがなより漢字のほうが適切なケース。<br>
        ◆句点を追加した方がいいケース。<br>
        ◆意味は通じるがより美しい日本語が有るようなケース。<br>
        ◆そもそも文が日本語として破綻しており、誤字脱字のレベルでないもの。'''

def demo(request):
    api = utils.Api()
    if request.method == 'POST':
        # テキストボックスに入力されたメッセージ
        textone = request.POST["textone"]
        sensitivity = request.POST["sensitivity"]
        # APIリクエストを投げてからの応答を取得
        rets,suggestions = api.get(textone,sensitivity)
        form = forms.UserForm(initial={'textone' : textone})
        
        c = {
             'form': form,
             'textone':textone,
             'message':message,
             'rets':rets,
             'suggestions':suggestions
        }
    else:
        # 初期表示の時にセッションもクリアする
        request.session.clear()
        # フォームの初期化
        form = forms.UserForm(label_suffix=':')

        c = {'form': form,
             'message':message
        }
    c.update(csrf(request))
    return render(request,'app/demo.html',c)

 

demo関数内でutils.Api()クラスからインスタンスを生成します。

api = utils.Api()

 

フォームから解析文章(textone)とチェック感度(sensitivity)の値を以下の部分で取得します。

textone = request.POST["textone"]
sensitivity = request.POST["sensitivity"]

 

api.getメソッドの引数に解析文章とチェック感度を与えてProofreading APIを使ってリクエストを投げます。
解析結果と提案内容が返ってくるのでそれぞれrets,suggestions変数に格納しておきます。

rets,suggestions = api.get(textone,sensitivity)

 

後は、取得した情報をテンプレートに返すだけです。

 

テンプレートの作成

 

base.html

<!doctype html>
<html lang="jp">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>

	{% block header %}
	{% endblock %}
    <title>{% block title %}デモ用テンプレート{% endblock %}</title>
  </head>
  <body>
	{% block content %}
	{% endblock %}
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
  </body>
</html>

普通のHTMLなので特段難しい点はないかと思います。

demo.html

{% extends '../base.html' %}
{% load bootstrap4 %}
{% load widget_tweaks %}

{% block title %}
  文章チェック機能
{% endblock %}

{% block content %}
<div class="container">

    <form action="" method="post">{% csrf_token %}
        <h1>文章チェック機能</h1>
        <div class="form-group row my-4">         
            <label class="col-lg-2 col-form-label"><h4>{{form.textone.label}}</h4></label><br>
    		<div class="col-lg-6">       
                {{form.textone|add_class:"form-control"}}

    		</div>
            <div class="col-lg-2">
                    {{form.sensitivity.label}}       
                    {{form.sensitivity|add_class:"form-control"}}
         </div> 
         <div class="col-lg-2">       
                <a href = "http://localhost:8000/app/demo/">入力文クリア</a>
               </div>
        </div>
        <div class="col-lg-2">       
                <button type="submit" class="btn btn-primary">実行</button>
            </div>
     </form>
     <hr>
     <div id="resultarea">


<div class="card border-dark mb-3 text-success">

<div class="card-body">
{{ message|safe }}
</div></div>
{% if rets %}
<h4>解析結果</h4><br>  
<div class="card mb-3">
 <div class="card-header">
 <div class="card-text">
                      
{{ rets|safe }}

<hr>
<b><指摘文字に対する改善候補></b><br>
{% for item in suggestions %}
<div class="card-text bg-primary text-white">
{{item.0}} >>{{item.1}}<br>
{% endfor %}
</div>
</div>
</div>
</div> 
{% endif %}

</div>
 </div>  
{% endblock %}

 

ベーステンプレート(base.html)とbootstrap4、widget_tweaksを利用するために以下を設定します。

{% extends '../base.html' %}
{% load bootstrap4 %}
{% load widget_tweaks %}

 

form.<フォーム名称>.labelでforms.pyで定義したフォームのラベル情報を表示します。

{{form.textone.label}}

 

以下はwidget_tweaksの機能で、form.<フォーム名称>|add_class:"form-control"}のように書くと、指定したフォームにCSSクラスを設定してくれます。

 {{form.textone|add_class:"form-control"}}

 

htmlタグを含む情報をWEBページに反映させるためにsafeを使っています。

{{ message|safe }}

 

指摘文字に対する提案内容を表示させる部分です。

{% for item in suggestions %}
<div class="card-text bg-primary text-white">
{{item.0}} >>{{item.1}}<br>
{% endfor %}

 

忘れずにマイグレーション(python manage.py migrate)しておきましょう。

 

http://127.0.0.1:8000/app/demo/ にアクセスすると以下のような画面が表示されます。
解析したい文章を入力し、チェックの厳密さを選択後に「実行」ボタンを押します。

以下のような解析結果が表示されます。
誤っていると思われる部分を背景黄色、赤文字でハイライト表示してくれます。
また、指摘文字に対するより正解と思われる候補を3つ提示してくれます。

 

まとめ

今回は、リクルートテクノロジーズが公開しているA3RTのproofreadingを使って簡単なAI機能を持ったWEBアプリケーションをDjangoで構築しました。

AIのロジック自体は準備されているAPIを利用するだけなので、比較的簡単にWEBアプリを構築することができました。
その他、実際に利用してみた所感は以下の通りです。

  • 明らかに間違っている変換ミスなどはしっかり検知してくれるが、すべての誤りを検知してくれるわけではないので、APIの仕様をしっかり理解したうえで利用する必要がある。
  • 複数単語の組み合わせとして考えると意味がちょっとおかしいが、それぞれの単語として意味が通じているようなケースではエラーとして指摘してくれないケースが多かった。
  • より望ましい候補を提案してくれる機能の精度はそこまで高くない印象を受けた。(自分で見て考えたほうが正確だし早い)

 

 

 

おすすめの記事