AI初心者がマーケティング記事の文章生成モデルを作成 + デモをして、学びを得た話

はじめに

はじめまして、データ戦略室の伊藤です。今年の5月にSOTに入社し、現在は商品の販売予測など、AI関連の案件に関わらせてもらっています。

私は機械学習の知識がほぼゼロに近かったのですが、外部の自然言語処理エキスパートへ自由に質問できる時間を設けてもらったり、ミニマムな学習モデルを作ってフィードバックをもらったりしていて、会社が育成に力を入れてくれていることを実感しています。(ありがたや…🤲)

今回はそのトライの一環として、Google Colabratoryでお手軽にGPT-2の文章生成モデルを作成し、他部署に共有するためのデモまでおこなった時の話をさせてもらえればと思います。

前提知識

ぼくが学んだAIプロジェクトの進め方

AIプロジェクトに取り掛かる上での考え方として、仕事ではじめる機械学習にも記載されているように、解決したいタスクのために機械学習を使う必要があるかを精査することが重要です。

しかし、私はAIプロジェクトの進め方をあまり分かっていなくて、最初から大量の学習データを用意して、大きな学習モデルを作ろうとしていました。

そのため、フィードバックとして「まずはもっと小さいタスクを設定して、AIがどれくらいタスクを解けるかを測って見てほしい」という言葉を受けて、ハッとしました。その言葉通りで、効果があるかも分からないのに「AIに解かせれば何とかなるだろう」という考えでは、駄目だった時の時間とコストが無駄になってしまいます。

いきなり技術と関係のない内容となってしまい恐縮ですが、AI初心者が陥りやすい失敗談を少しでも共有できればという思いと、この失敗をしないためにまずは簡単なプロトタイプを作ってステークホルダーに見てもらうことが大切だという考えをお伝えしたく、この話をさせていただきました。

Google Colabratoryとは

Google Colaboratory(以下「Colab」)は、環境構築なしで使えるブラウザのPython環境です。

Colabでは無料枠の中でも、ある程度自由にGPUを使うこともできます。

もちろん、本格的な学習モデルを作成しようと思ったらVertex AIなど別の手段が挙がってきますが、サクッと学習をしてみたい場合や、上述のようなプロジェクトの初期段階でAIを使う効果があるのかを測りたい場合などには、良い選択肢になると思います。

Google Colaboratory

GPT-2とは

GPT-2は2019年2月にOpenAIから発表されたテキスト生成モデルです。入力としてテキストを与えると、それに続くテキストを生成することができます。

こちらの例が分かりやすかったので拝借しますと、「吾輩は猫で」というテキストに対して、それに続く単語として最も確率が高いのは「ある」だと予測することによって、「吾輩は猫である」というテキストを生成します。

また、このモデルは巨大な文章データを使って事前学習しているため、少ない学習データで解きたいタスクに合わせた微調整(ファインチューニング)をすることも可能です。今回のプロトタイプを作成するにあたっても、riina社が提供してくれている事前学習モデルをベースに、より目的に沿った文章を生成するためのファインチューニングをおこなっています。

学習モデルの作成手順

今回試してみたことは、マーケティング記事のドラフト生成です。AIがドラフト記事を自動生成することによって、人間が0から記事を制作する時と比較して、作業時間を短縮することができないかと考えてみました。

ファインチューニングの手順は、こちらの記事を参考にさせていただきました。記載の手順に沿って、必要なパッケージやTokenizerをインストールしていきます。

なお、学習データの取得・準備は要件によって手順が異なりますので、あくまで一例として今回おこなった処理を記載します。

▼学習データの取得・準備

  • 文頭に開始トークン(<s>)と、文末に終了トークン(</s>)を追加
  • 見出しが切り替わる箇所に区切りトークン([SEP])を追加
  • 空白文字や特殊文字の削除
  • 体言止めの文章を用言止めの文章に修正
  • データセットを分割して、学習データをtrain.txtに、検証データをvalidation.txtに保存

▼パッケージのインストール

!pip install -q sentencepiece
!pip install -q git+https://github.com/huggingface/transformers
!pip install -q datasets

▼Tokenizerのインストール

from transformers import T5Tokenizer, GPT2LMHeadModel

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")
tokenizer.do_lower_case = True

▼リポジトリのクローン(一度だけ)

!git clone https://github.com/huggingface/transformers

▼ファインチューニングの実行

%%time

!python ./transformers/examples/pytorch/language-modeling/run_clm.py \
    --model_name_or_path=rinna/japanese-gpt2-medium \
    --train_file=train.txt \
    --validation_file=validation.txt \
    --do_train \
    --do_eval \
    --num_train_epochs=10 \ #訓練のエポック数
    --save_steps=5000 \ #checkpointを何ステップ毎に保存するか
    --save_total_limit=2 \ #checkpointを何個保存するか、上記3パラメータは環境に合わせて調整
    --per_device_train_batch_size=1 \
    --per_device_eval_batch_size=1 \
    --output_dir=./output/ \
    --use_fast_tokenizer=False

ファインチューニングが完了すると、output_dirで指定したディレクトリ(上の例ではoutputディレクトリ)にモデルが出力されます。

文章生成の動作確認

▼学習後のモデルをロード

load_model = AutoModelForCausalLM.from_pretrained("./output/")
load_model.eval()

▼予測前後の処理

import re

def generate_text(model,input_text,max_length=500,min_length=100,repetition_penalty=1.2,temperature=0.7,top_k=500,top_p=0.95):
  token_ids = tokenizer.encode("<s>" + input_text, add_special_tokens=False, return_tensors="pt")
  output_ids = model.generate(
        token_ids.to(model.device),
        max_length=max_length,
        min_length=min_length,
        do_sample=True,
                repetition_penalty=repetition_penalty, #繰り返しペナルティのパラメータ
        temperature=temperature, #単語間の確率サンプリングの度合い/高いほどサンプリングに制限がかかり、文章の一貫性が増す
        top_k=top_k, #上位○個の単語ベクトルに絞りこむ/高いほど文章のランダム性が増す
        top_p=top_p, #前後の単語との組み合わせが確率○を超えるものに絞り込む/高いほどしっかりとした文章になる代わりに、同じような表現が続く
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id,
        bad_word_ids=[[tokenizer.unk_token_id]]
  )
  output_text = tokenizer.decode(output_ids.tolist()[0])
  # 詳細は省略しますが、デコード後に開始トークンや終了トークンなどの固有トークンを除外、変換する処理もおこなっています。
  
  return output_text

(追記: トークンの変換処理時にtypoをしていたことが発覚したため、以下の生成文章を一部修正しております🙇)

generate関数にパラメータを与えるほか、入力文章に開始トークンを追加してから予測をおこなったり、生成文章からは各種トークンを削除したりする処理をおこなっています。

では、実際に一行目の文章を与えて、それに続く文章を生成してみます。

input_text="リスティング広告とは、ユーザーが検索したキーワードに合わせて表示される広告のことです。"
output_text = generate_text(load_model,input_text)

▼生成文章①

リスティング広告とは、ユーザーが検索したキーワードに合わせて表示される広告のことです。
代表的な媒体はGoogleとYahoo!の2つで、いずれも無料で利用できます。
リスティング広告の特徴は主に以下の3点です。
メリットが多くデメリットはあまりないので、初めてリスティング広告を利用する方でも簡単に始められます。

 リスティング広告には、検索結果画面に広告文が表示されるため、「検索エンジンを利用しない層へのリーチ」が可能です。
パソコンやスマートフォンなどインターネットを利用しているユーザーは、能動的に情報を探していることから、すぐに商品・サービスに興味をもつことができ、その分購買意欲が高まる傾向があります。
すでにニーズが顕在化している場合は、クリック単価が低くコンバージョン率が高いため、費用対効果が高くなる可能性も高いでしょう。
ただし、あまり知られていない「ニッチな市場」などでは、そもそも検索されていないために表示回数が少なく、コストパフォーマンスが低い場合があります。
このようなときは、予算を引き上げるべきか撤退すべきか判断が難しい場合もあるでしょう。
自社の状態と相談したうえで慎重に検討しましょう。
また、すでにニーズが顕在化していて、リスティング広告経由でアプローチしたい潜在顧客がいる場合には、そのターゲットにもアプローチできる機能(サーチターゲティング)が搭載されているものを選ぶと良いでしょう。

▼生成文章②

リスティング広告とは、ユーザーが検索したキーワードに合わせて表示される広告のことです。
例えば、「ダイエット」と検索している人は「食事制限」「芸能人痩せる方法」というようなニーズがあると考えられますので、そのような人にアピールできるのが特徴です。
また、GoogleやYahoo!などのサービスでは提携するサイトも多く、特定の地域にいる人や興味関心のある人に対して幅広くアプローチができます。
そのため、費用対効果が高くなりやすいという特徴もあります。
インターネットを使えば24時間365日集客できるため、店舗型ビジネスや緊急性の高いビジネスでも有効的といえるでしょう。
なお、近年ではスマートフォンなどの普及により、パソコンを使わずスマホだけでホームページを運営しているケースも増えています。

 リスティング広告は簡単に始められる一方で、運用するにあたって一定の知識が必要となります。
特に、はじめて運用する場合には覚えることばかりで戸惑うことも多くなるでしょう。
そこで本記事では、リスティング広告を始める前に知っておきたい基礎知識を紹介していきます。

ある程度不自然さは残りますが、最低限日本語として読めるレベルの文章になっているのではないでしょうか。文章内容についても、「代表的な媒体はGoogleとYahoo!の2つ」のように、しっかり正しい知識を学習できているように感じます。

デモ

学習モデルが完成したら、よりドメインに詳しいステークホルダーに、実用性があるかどうかを見てもらう必要があります。ただ、デモの時にセル内の変数を弄って入力文章を変えたりするのは、どうにも分かりやすさが欠けているように感じてしまいます。

そこで使いたいのが、Form機能です。この機能を使えば、ColabにUIを表示することができます。

セル右上の「その他のセル操作」⇨「フォーム」⇨「コードを非表示」から、コードを消してフォームのみを表示することも可能です。

UIから最初の一行を入力できるようにしたことで、その場で「『〇〇〜』から始まる文章を生成して見て欲しい」という流れになった時も、スムーズに分かりやすい形でデモができたと思います。

ちなみに、Flask + Next.jsで簡単なWebアプリも作ってみましたが、小一時間ほどかけた割にはColab上のデモと比べてほとんど分かりやすさは変わらなかった印象です。時間対成果を考えると、個人的にはColabでデモもおこなうのをオススメしたいです。

おわりに

  • AIプロジェクトの初期段階では、まずAIを使う効果があるかを精査しよう
  • ColabやGPT-2の事前学習モデルなど、用意された環境をフル活用しよう
  • ColabのForm機能はいいぞ

これから、より会社に貢献できるように試行錯誤をしていきますので、結果が出たら、また次回の記事などで報告できればと思います。

最後まで読んでくださり、ありがとうございました。