GPT-3を使ってトレンドワードから歌詞を生成し、楽曲を作成するアプリを作ってみた

## はじめに
本記事はAidemy Premium PlanのAIアプリ作成コースの修了要件となる成果物についての投稿となります。
初心者かつこういった投稿も初めてですので粗雑な内容についてはご容赦いただき、問題点等ございましたらご指摘いただければ幸いでございます。
## 自己紹介
経理職を中心としたバックオフィス業務に従事しておりましたが、現在は転職に向け機械学習を中心に学んでおります。
3年程前に独学でPythonを学び、業務効率化を中心に細々と利用しておりました。
今まではローカル環境での利用が中心でしたので、今回のアプリ制作においてGitの利用やデプロイ作業など慣れていなかった作業でハマる事が多く、良い学びになりました。
## アプリ制作の経緯
どういったアプリを制作するかについては悩み、迷走したりして余計な時間を費やしてしまった気がします。
思いつくものはありきたりで既存のものの縮小再生産となる様なものばかりでしたが、どうせ作成するならなるべく自分が見たことの無いアプリの方がモチベーションの維持に良いかと考えました。
最終的にはちょうどChatGPTが話題となっていた事もあり、私自身も興味がわきましたのでAPI利用が可能なGPT-3を利用することにしました。
### ◆GPT-3(Generative Pretrained Transformer) とは
AI研究機関のOpenAIが開発したTransformerベースの自己回帰言語モデルで、まるで人が書いたように自然な文章を作成することが出来る。
WikipediaなどのWebを中心におよそ45TBという膨大なテキストデータを基に事前学習し、1750億個のパラメータを持つ巨大な言語モデル。
文章や記事の作成、質問への応答、自然要約、デザインやプログラミングのソースコードの作成などかなり広域な分野で高精度な利用が可能。
2022年11月30日にはGPT-3の改良版であるGPT-3.5を採用した「ChatGPT」がリリースされ大きな話題となった。

https://openai.com/

## アプリの概要
[【本アプリへのリンク】](https://songwriter-app.onrender.com)
>本アプリでは下記ウェブサービスを利用させていただきました。
>+ トレンドワードの取得: [Twittrend（ついっトレンド） - 各地域のTwitterのトレンド](https://twittrend.jp/ "Twittrend（ついっトレンド） - 各地域のTwitterのトレンド")
>+ 自動作曲: [CREEVO 自動作曲（作曲AI）](https://creevo-music.com/ "CREEVO 自動作曲（作曲AI）")
### ■実行環境
メイン部分は Python + Flask + Render で作成しました。
<details><summary><strong>◆ライブラリ一覧</strong></summary>

+ python==3.10.8
+ beautifulsoup4==4.11.1
+ brotlipy==0.7.0
+ certifi==2022.9.24
+ cffi==1.15.1
+ charset-normalizer==2.1.1
+ click==8.0.4
+ colorama==0.4.5
+ cryptography==38.0.1
+ et-xmlfile==1.1.0
+ Flask==2.1.3
+ idna==3.4
+ itsdangerous==2.0.1
+ jaconv==0.3.3
+ Jinja2==3.1.2
+ MarkupSafe==2.1.1
+ numpy==1.21.6
+ openai==0.25.0
+ openpyxl==3.0.10
+ packaging==22.0
+ pandas==1.3.5
+ pandas-stubs==1.2.0.62
+ pycparser==2.11
+ pyOpenSSL==22.0.0
+ PySocks==1.7.1
+ python-dateutil==2.8.2
+ python-dotenv==0.21.0
+ pytz==2022.7
+ requests==2.28.1
+ six==1.16.0
+ soupsieve==2.3.2
+ tqdm==4.64.1
+ types-pytz==2022.7.0.0
+ typing_extensions==4.4.0
+ urllib3==1.26.13
+ webdriver-manager==3.8.5
+ Werkzeug==2.0.3
+ win-inet-pton==1.1.0
+ wincertstore==0.2
</details>

<details><summary><strong>◆AWS Lambda実行環境</strong></summary>

+ python==3.7.15
+ selenium==3.141.0
</details>

#### ◆ディレクトリ構成
```
root/
├ app.py
├ song_writer.py
├ static
│   └css
│
└ templates
　   ├ base.html
　   ├ finished_page.html
　   ├ index.html
　   ├ waiting_lyric.html
　   └ waiting_song.html
```
### ●おおまかな流れ
**1. トレンドワードをスクレイピングして取得**
　➥ BeautifulSoupを利用し現在のトレンド1位のワードを取得
**2. 取得したトレンドワードをタイトルにしてGPT-3で歌詞を作成**
　➥ OpenAIのAPIを叩きGPT-3を利用
**3. 作成された歌詞からCREEVOで作曲**
　➥ AWS LambdaからSeleniumを起動しCREEVOを利用
### ■GPT-3のAPI利用手順
まずはOpenAIのアカウントが無ければ、[こちらのURL](https://openai.com/api/)の「Get Started」からアカウントを作成。
OpenAIへログイン後、[API Key作成画面](https://beta.openai.com/account/api-keys)にて新たなSecret Keyを発行。（APIの利用については$18の無料枠が与えられる）
Python側の処理としてはpipコマンドで「openai」ライブラリをインストール。
```
pip install openai
```
【前準備】
今回はRenderの環境変数にAPI Keyを記載したので`os.getenv()`から取得
```Python
import os
import openai

API_KEY = os.getenv('OPEN_AI_API_KEY')  # 環境変数から取得
openai.api_key = API_KEY
```
プロンプトを渡し文章生成を行う。
```Python
prompt = f'「{title}」をタイトルとした日本語の歌詞を考えてください。'
response = openai.Completion.create(engine='text-davinci-003',
                                    prompt=prompt,
                                    max_tokens=1024,
                                    temperature=0.8)
lyric = response['choices'][0]['text']
```
#### ◆`openai.Completion.create()`のパラメータ
<details><summary><strong>一覧</storong></summary>

+ **engine**
使用するエンジン。
    + davinci
GPT-3の中で最も精度が高く低速。複雑な文章生成などに向いている。
    + curie
davinciに次いで高精度で、davinciより高速、低価格。センチメント分析や翻訳などに向いている。
    + babage
非常に高速で低価格。簡単なテキスト分類に向いている。
    + ada
最も高速で低価格。単純な分類などが可能。
+ **prompt**
インプットする文章。
+ **max_tokens**
生成するトークンの最大数（デフォルト:16、最大:2048）
+ **temperature**
出力する単語のランダム性。
0から2の範囲を取る。
0であれば完全に確定的な文章を出力するので、毎回同じ文章を生成する。
2であれば完全にランダムに次の単語、次の単語と選ぶので、意味の通らない文章になる。
top_pと同時変更は非推奨（デフォルト:1）
+ **top_p**
nucleusサンプリング。
小さくすればするほど候補が確率の高いものに絞られるため、確定的になる。
大きくすれば色んな単語から選ぶようになるので、よりランダム性が強くなる。
temperatureと同時変更は非推奨（デフォルト:1）
+ **frequency_penalty**
生成したテキストに基づいて、新しいトークンに課すペナルティ。
同じテキスト出力の可能性を減らす。（デフォルト:0）
+ **presence_penalty**
生成したトークンに基づいて、新しいトークンに課すペナルティ。
同じトークンの繰り返し可能性を減らす。（デフォルト:0）
+ **best_of**
サーバー側でbest_of個の結果を生成し、最良を返す。（デフォルト:1）
+ **stop**
どんな単語が出現したら文章生成を打ち切るかを指定する。最大4つ（デフォルト:null）
+ **n**
生成する結果の数。（デフォルト:1）
+ **echo**
Trueにするとpromptに入力されている文章も出力する。（デフォルト:false）
+ **stream**
Tureにすると進行状況をストリーミングバックする。（デフォルト:false）
+ **logprobs**
選択トークンだけでなく、可能性の高いトークンにログ確率を含める。（デフォルト:null）
+ **logit_bia**
指定したトークンの可能性を減らす。（デフォルト:null）
</details>

### ■AWS LambdaからSeleniumを起動してCREEVOを利用
AWS LambdaでのSelenium利用環境の構築については、大部分を下記の記事より参照させていただきました。

https://dev.classmethod.jp/articles/aws-lambda-python-selenium-make-env/

AWS Lambdaで環境を構築した後、Lambdaの関数URLを有効にしてhttpアクセスでGPT-3が作成した歌詞を送信。
（JSONペイロードをPOST送信したがデコードが上手くいかなかったのでクエリパラメータで送信）
```Python:app
import requests

def make_song_in_aws(title, lyric):
    aws_lambda_url = 'Lambdaの関数URL'
    aws_lambda_url += f'?title={title}&lyric={lyric}'
    res = requests.get(aws_lambda_url)
    return res.text
```
【Lambda側での引数受取】
```Python:Lambda
def lambda_handler(event, context):
    title = event['queryStringParameters']['title']
    lyric = event['queryStringParameters']['lyric']
```
## 実行結果
【HOME画面】
![home.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630236/18a786e2-9336-5c4f-d9b8-8d7eae88dcff.png)
【実行後画面】
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630236/cba8c3ff-c023-7c4d-3c55-312bbaecf45c.png)
### ●結果の考察
+ GPT-3が生成する歌詞は基本的にポジティブな内容が多い印象を受ける。
+ CREEVOで生成される楽曲は女性のボーカルで、曲調については細かく指定が可能だが今回はランダムで作成している為、結果としては様々。
+ CREEVOでの楽曲作成は最大12小節までなので、歌詞に対し楽曲は短くなる。
+ CREEVOの仕様により処理の完了までには数分を要する。

## 所感
今回のアプリ制作では、コア部分のバックエンド処理については特に問題なく1日程度で書けたが、Flaskのフロントエンド処理やデプロイ作業等は不慣れな事もあり、つまずくことも多く余計な時間がかかってしまった。
今まで個人でこういったアプリ制作を行う機会もなかったので、学びとなる部分が多かった。
今回の学びを足掛かりに、今まで触れられなかった分野についても積極的に学習の機会を増やしていきたいと感じた。
## 終わりに
今回はAidemyで履修した範囲を活かしきれないアプリだったかと思うので、今後の目標としてはKaggle等のコンペに取り組むなどして機械学習分野の知見を深めていきたい。