Node.jsでチャットボット

Node.jsでチャットボットを作る方法です。 以下で紹介するサンプルでは、チャットボットにテキトーに機械学習 (multi-label classification) させて、ユーザからの質問に近い情報を返すプログラムです。 サンプルコードはローカルだけで完結しています。

目次:

前提

  • node.js バージョン: v8.11.3
  • limdu npmモジュールを利用

サンプルコード

以下のサンプルコードはゴミの分別をアドバイスするチャットボットです。 ユーザから入力されたゴミの種類を元にゴミの出し方を出力します。

/**
 * multilabel-classification-example.js
 * 
 * 使用例:
 * $ node multilabel-classification-example.js 
 */

// multi-label classification対応のマシンラーニングモジュール limdu をインポート
const limdu = require("limdu");

// ユーザ入力をインタラクティブに受け付けるため
const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

//
// 篩(ふるい)をセットアップ
// 
const MyWinnow = limdu.classifiers.Winnow.bind(0, {
  retrain_count: 10
});

//
// 分別機能をセットアップ
//
const intentClassifier = new limdu.classifiers.multilabel.BinaryRelevance({
  binaryClassifierType: MyWinnow
});

//
// トレーニング
// NOTE: 2gramに分かち書きしてトレーニングしてみた
// 
intentClassifier.trainBatch([{
  input: create2gramInput("空き缶、空き瓶、ペットボトル、新聞紙、雑誌、ダンボール(段ボール)"),
  output: "「資源ごみ」としてお出しください(空き缶、空き瓶、ペットボトル、新聞紙、雑誌、ダンボール(段ボール)など)"
}, {
  input: create2gramInput("生ごみ、アルミホイル、貝がら、衣類、布類 汚れた紙など 木製品・木材、製品プラスチック、ゴム・革・ビニール製品"),
  output: "「燃やせるごみ」としてお出しください(生ごみ、アルミホイル、貝がら、衣類、布類 汚れた紙など 木製品・木材、製品プラスチック、ゴム・革・ビニール製品など)"
}, ]);

//
// ユーザからの入力を受付
//
quest();

function quest() {
  rl.question('ごみの出し方をアドバイスします。どんなごみを捨てますか?(例: ペットボトル) ', (answer) => {
    //
    // 分別実施
    // NOTE: ユーザの入力値を2gramに分かち書きして投入
    // 
    const input2gram = create2gramInput(answer);
    const result = intentClassifier.classify(input2gram);
    if (result.length >= 1) {
      console.log("回答: " + result[0]); // e.g."「資源ごみ」としてお出しください(空き缶、空き瓶、ペットボトル、新聞紙、雑誌、ダンボール(段ボール)など)"
    } else {
      console.log(`回答: ごめんなさい、"${answer}" の出し方は分かりません。`);
    }
    // rl.close();
    quest(); // 再帰質問
  });
}

/**
 * 入力された文章を2gram分かち書きし、
 *  { "xx": 1, ... } なフォーマットのオブジェクトを返します。
 *  
 * @param  {String} e.g. "空き缶、空き瓶、ペットボトル、新聞紙、雑誌、ダンボール(段ボール)"
 * @return {Object} e.g. { '空き': 1,
 *  'き缶': 1,
 *  'き瓶': 1,
 *  'ペッ': 1,
 *  'ット': 1,
 *  'トボ': 1,
 *  'ボト': 1,
 *  'トル': 1,
 *  '新聞': 1,
 *  '聞紙': 1,
 *  '雑誌': 1,
 *  'ダン': 1,
 *  'ンボ': 1,
 *  'ボー': 1,
 *  'ール': 1,
 *  '段ボ': 1 }
 */
function create2gramInput(s) {
  const ret = {};
  const arr = s.split("");
  for (let len = arr.length, i = 0; i < len; i++) {
    const c0 = arr[i]; // 2gramの1文字目
    const c1 = arr[i + 1]; // 2gramの2文字目
    // 1文字目or2文字目のどちらかに記号や区切り文字が入ってれば無視
    if (shouldIgnore(c0) || shouldIgnore(c1)) continue;
    ret[c0 + c1] = 1;
  }
  return ret;

  // 記号や区切りチェック
  function shouldIgnore(c) {
    if (!c) return true;
    return / | |,|、|\.|。|・|:|:|\(|\)/.test(c);
  }
}

NOTE: 上記コードの「トレーニング」と「分析」の処理では文章を2gram化し日本語マッチの精度を上げています。

実行例

$ node multilabel-classification-example.js

ごみの出し方をアドバイスします。どんなごみを捨てますか?(例: ペットボトル) ペットボトル
回答: 「資源ごみ」としてお出しください(空き缶、空き瓶、ペットボトル、新聞紙、雑誌、ダンボール(段ボール)など)

ごみの出し方をアドバイスします。どんなごみを捨てますか?(例: ペットボトル) 生ごみ
回答: 「燃やせるごみ」としてお出しください(生ごみ、アルミホイル、貝がら、衣類、布類 汚れた紙など 木製品・木材、製品プラスチック、ゴム・革・ビニール製品など)

ごみの出し方をアドバイスします。どんなごみを捨てますか?(例: ペットボトル) あああ
回答: ごめんなさい、"あああ" の出し方は分かりません。

以上です。

オススメ:Zattoyomiで時事ネタチェックの時間節約!
芽萌丸プログラミング部 @programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。記事は主に @TanakaSoftwareLab が担当。