あかんわ

覚えたことをブログに書くようにすれば多少はやる気が出るかと思ったんです

恋するプログラムをSinatraでWebアプリにするPart.10[CHAPTER7 学習のススメ]③

記事の概要

『恋するプログラム』の[CHAPTER7 学習のススメ]を参考に、形態素解析を利用して応答時の語彙を増やすチャットボットを作成します。

目次

開発環境

参考記事
Rubyの開発環境構築は、こちらの記事を参考にさせていただきました。

ノビィの辞書の種類を増やす

入力した会話の内容をランダム辞書に追加しつつ、形態素解析を利用して名詞を抽出し、名詞をキーワードにした応答をパターン辞書へ、名詞を除外した文の骨組みをテンプレート辞書へ追加します。

アプリケーションディレクトリの構成
~/programinlove
    |- proto.rb                 // CUIチャットボットのメインファイル
    |- unmo.rb                  // チャットボットオブジェクトのモデル
    |- responder.rb             // 応答オブジェクトのモデル
    |- dictionary.rb            // 辞書を読み込み管理するクラス
    |- mecab_natto.rb           // 形態素解析器MeCabを使うためのモジュール
    |- morph.rb                 // MeCabNattoモジュールをunmo.rbで使うためのモジュール
    |- log.txt                  // ユーザの入力履歴を保存するログファイル
    |- /dics                    // 辞書ファイルを格納するディレクトリ
    |     |- random.txt         // ランダムな応答を返すための辞書
    |     |- pattern.txt        // パターンに合った応答を返すための辞書
    |     |- template.txt       // テンプレートを使った応答を返すための辞書
    |- noby.rb                  // Webアプリのメインファイル
    |- /views                   // ビューのテンプレートを配置するディレクトリ
    |     |- index.erb          // チャットのインターフェースを表示するビュー
    |- /public                  // 静的ファイルを配置するディレクトリ
          |- nobycanvas.js      // キャラクターの画像をアニメーションさせるJavascript
          |- styles.css         // チャットのインターフェースをレイアウトするcss
          |- /img               // 画像ファイルを格納するディレクトリ
               |- /normal       // 平常時の画像を格納するディレクトリ
               |- /blink        // 瞬きの画像を格納するディレクトリ
               |- /lookaround   // 周囲を見回す画像を格納するディレクトリ
               |- /talk         // 喋る画像を格納するディレクトリ
               ~ 以下に表情ごとの画像を格納するディレクトリを配置
ソースコード

morph.rbからmecab_natto.rbを呼び出し、形態素解析の結果を配列に格納します。

サンプルプログラムと異なり形態素解析MeCabを使用しているため、init_analyzerメソッドとanalyzeメソッドの中身をMeCabの出力に合わせて変更しています。
Morphモジュールの初期化は、noby.rbコンフィギュレーションMorph::init_analyzerメソッドを呼び出して行ないます。

その他のRubyファイルは、サンプルプログラムと同様の記述で、特に変更はありません。

morph.rb
require_relative 'mecab_natto'

module Morph
  def init_analyzer
    MeCabNatto::setarg('-F%m\s%F-[0,1,2]')
  end

  def analyze(text)
    # 形態素解析の出力を表層形と品詞を要素にした配列にして
    # 解析結果の配列に格納する
    analysis_result = []
    MeCabNatto::analyze(text).each do |part|
      analysis_result.push(part.feature.split(/ /)) if !part.is_eos?
    end

    return analysis_result
  end

  def keyword?(part)
    return /名詞-(一般|固有名詞|サ変接続|形容動詞語幹)/ =~ part
  end

  module_function :init_analyzer, :analyze, :keyword?
end
noby.rb
require 'sinatra'
require 'sinatra/reloader'

require_relative 'unmo'


# 会話ログを格納する配列
log_area = []

# 起動時にオプションを設定する
# 状態を保持するオブジェクトの生成に使う
configure do
  # ノビィ生成
  set :noby, Unmo.new('noby')
  # 形態素解析のためにMorphモジュールを初期化
  set :morph, Morph::init_analyzer
end

# ヘルパーメソッドを定義する
# ルーティングメソッドの中で使う
helpers do
  def noby
    # ノビィへアクセス
    noby = settings.noby
  end

  def prompt(resp_opt)
    # 応答を表示する際のプロンプトを作成
    resp_opt ? "#{noby.name}#{noby.responder_name}" : "#{noby.name}"
  end

  def change_looks
    # 感情値で表情を変化させる
    case noby.mood
    when -5..5 then 'talk'
    when -10..-5 then 'angry_talk'
    when -15..-10 then 'more_angry_talk'
    when 5..10 then 'happy_talk'
    when 10..15 then 'more_happy_talk'
    end
  end

  def save_log(log)
    # ファイルにログを書き込む
    noby.save

    open('log.txt', 'a') do |f|
      f.puts(log)
    end
  end
end

# URL'/'にアクセス
get '/' do
  # 日時情報をログに格納
  log = "\nUnmo System : #{noby.name} Log -- #{Time.now}"
  save_log(log)
  # 会話ログを初期化してnobyfomを表示
  log_area = []

  erb :index
end

# URL'/'にPOSTメソッドでアクセス
post '/' do
  # ユーザの入力を取得
  talk_text = params['inputarea']

  # Responderを表示するチェックボックスの状態を取得
  # チェックされてる場合は状態を維持
  resp_opt = params['respoption']
  @check = "checked" if resp_opt

  # ユーザの入力があれば応答して会話ログに表示
  unless talk_text.empty?
    @responder_resp = noby.dialogue(talk_text)
    log_area << "> #{talk_text}<br>"
    log_area << "#{prompt(resp_opt)}> #{@responder_resp}<br>"
    @noby_state = change_looks

    save_log(talk_text)
  end

  @talk_log = log_area.join

  erb :index
end

このコードのコミットには、[chapter7-4]のタグが付いてます。

実行結果

ターミナルでnoby.rbを動かし、ブラウザにhttp://localhost:4567を入力します。

~/programinlove
$ruby noby.rb

f:id:b0npu:20160107142020p:plain

会話に含まれていた名詞が、会話の内容と共にパターン辞書に保存され、会話の内容から名詞を除外した文の骨組みがテンプレート辞書に保存されます。

f:id:b0npu:20160228200728p:plain

0##すもも 0##すもももももももものうち
0##もも   0##すもももももももものうち
0##私  0##私はプログラムの女の子です
0##プログラム  0##私はプログラムの女の子です
0##女の子    0##私はプログラムの女の子です
3    %noun%も%noun%も%noun%のうち
3   %noun%は%noun%の%noun%です

参考書籍

参考記事

Sinatraについては、こちらの記事を参考にさせていただきました。

関連記事

- 恋するプログラムをSinatraでWebアプリにするPart.0[はじめに]
- 恋するプログラムをSinatraでWebアプリにするPart.1[CHAPTER3 ほんとに無能]
- 恋するプログラムをSinatraでWebアプリにするPart.2[CHAPTER4 あこがれのGUI]①
- 恋するプログラムをSinatraでWebアプリにするPart.3[CHAPTER4 あこがれのGUI]②
- 恋するプログラムをSinatraでWebアプリにするPart.4[CHAPTER4 あこがれのGUI]③
- 恋するプログラムをSinatraでWebアプリにするPart.5[CHAPTER5 辞書を片手に]
- 恋するプログラムをSinatraでWebアプリにするPart.6[CHAPTER6 感情コントロールの魔術師]①
- 恋するプログラムをSinatraでWebアプリにするPart.7[CHAPTER6 感情コントロールの魔術師]②
- 恋するプログラムをSinatraでWebアプリにするPart.8[CHAPTER7 学習のススメ]①
- 恋するプログラムをSinatraでWebアプリにするPart.9[CHAPTER7 学習のススメ]②
- 恋するプログラムをSinatraでWebアプリにするPart.11[CHAPTER8 文章を作り出す]
- 恋するプログラムをSinatraでWebアプリにするPart.12[CHAPTER9 ノビィ、ネットワークにつながる]
- 恋するプログラムをSinatraでWebアプリにするPart.13[おわりに]