🎉

Streamlitで音声文字起こしアプリを作成してみよう!*文字起こし結果修正機能付き

2023/09/08に公開

皆さん、ChatGPTをご利用でしょうか?ChatGPTは多様なタスクをこなすことができ、その中には議事録の作成も含まれています。その能力の背後には、大規模言語モデル(LLM)としての訓練があります。そのため、文章の要約や整理が得意です。

文章を要約する際、手で打ち込む方法もありますが、会議を録音してそれを文字起こしするアプローチも非常に効率的です。そのための強力なツールとして「Whisper」が存在します。

今回は、このWhisperをStreamlitで実装し、その文字起こし結果をChatGPTで修正する機能を持つアプリケーションの作成方法をご紹介します!

一番下にgithubとstreamlitのリンクを貼っています!


音声文字起こし Whisper

多くの企業が音声文字起こしサービスを提供していますが、中でもOpenAIの「Whisper」はコスパに優れているため、特におすすめです。

Whisperは、Hugging Faceのプラットフォームでオープンソースとして公開されています。このため、ローカルPCでの利用も可能です。ただし、大量のデータや長時間の録音を文字起こしする場合、処理には相応の時間がかかります。もし迅速な結果を求める場合は、WhisperのAPI利用が適しています。
https://openai.com/research/whisper
値段は記事公開時点で0.006$/分です。他サービスと比べれば安いのですが、1時間とかの録音データを何回も文字起こしするとそれなりの値段になってきますのでご注意を。

必要なライブラリのインストール

pip install openai
pip install streamlit
pip install langchain
pip install typing

APIでWhisperを実行してみよう!

以下は公式ドキュメントです。
https://platform.openai.com/docs/guides/speech-to-text/quickstart
音声文字起こしをするためのコードは下記でシンプルです。

import openai
openai.api_key = 'your-api-key-here'
audio_file= open("/path/to/file/audio.mp3", "rb")
transcript = openai.Audio.transcribe("whisper-1", audio_file)["text"]
print(transcript)

音声ファイルは何でも良いですので、特に文字起こししたいファイルが無ければ以下リンクからダウンロード出来ます。
https://amitaro.net/voice/voice_dl/
私は上記リンクから「ちょっと難しそうだけど頑張ってみよう」というファイルをダウンロードしました。以下は実行結果です。

ちょっと難しそうだけど頑張ってみよう

完璧です👍音声そのまま文字起こししてくれました。
 
これをstreamlitで実装させてみましょう!

Streamlitで実装させよう!

import openai
import streamlit as st
openai.api_key = 'your-api-key-here'
st.header('音声文字起こしアプリ')

upload_file = st.file_uploader('音声文字起こしするファイルを選択してください  \nAPIの上限により25MB以上のファイルは文字起こし不可です。\
                               ファイルを分割する等容量を少なくしてください', type=['m4a', 'mp3', 'webm', 'mp4', 'mpga', 'wav'])
if upload_file is not None:
    st.subheader('ファイル詳細')
    file_details = {'FileName': upload_file.name, 'FileType': upload_file.type, 'FileSize': upload_file.size}
    st.write(file_details)
    file_name=upload_file.name.split('.')[0]
    if upload_file.size > 25000000:
        st.error('エラー:ファイルが25MBを超えています。APIの上限により25MB以上のファイルは文字起こし不可です。ファイルを分割する等容量を少なくしてください', icon="🚨")

if st.button('文字起こし開始',key="w_button"):
    if upload_file is None:
        st.error('エラー:ファイルが選択されていません', icon="🚨")
    else:
        with st.spinner('***音声文字起こしを実行中です...***'):
            trans= openai.Audio.transcribe("whisper-1" ,upload_file)["text"]
        st.success('***音声文字起こしを完了しました***')
        st.write(trans)

大事な所は説明しますね!

upload_file = st.file_uploader('音声文字起こしするファイルを選択してください・・・'),
       type=['m4a', 'mp3', 'webm', 'mp4', 'mpga', 'wav'])

st.file_uploaderでファイルをアップロードすることが出来ます!typeでファイルの種類を指定する事が出来ます。

if upload_file.size > 25000000:
	st.error('エラー:ファイルが25MBを超えています・・・', icon="🚨")

APIのファイル上限は25MBとなっています。それ以上のファイルをアップロードした時にはエラーメッセージを表示するようにしています。
 
25MBというの大体音声ファイルなら15分とかですかね!容量削減の為にファイルの分割、動画ファイルなら音声の抽出といったことが必要になってきます!
容量削減のアプリも今後公開していきますね。

with st.spinner('***音声文字起こしを実行中です...***'):
    trans= openai.Audio.transcribe("whisper-1" ,upload_file)["text"]

Whisperを使用して音声の文字起こしを行っています。with st.spinner(...)を使用することで、文字起こしの処理が実行中であることをユーザーにメッセージとして表示することができます。
 
さて、音声文字起こしの結果を手に入れることができました。しかし、文字起こしの精度はいくつかの要因、例えば音声ファイルのノイズ、話者の声の大きさ、または発声の明瞭さによって左右されます。特に日本語の場合、英語と比べると学習データが少ないため、実際の会話の録音を文字に起こすと、精度が高いとは言えないことがあります。ただし、技術の進歩とともにこれは今後改善されることでしょう。

このような背景から、音声文字起こしの結果を更に精度高くするために、大規模言語モデル(LLM)を用いて修正を行う手段が求められます。それでは、この修正機能の実装方法について見ていきましょう!

文字起こし結果修正機能を実装しよう!

文字起こし結果をChatGPTにAPI経由で渡す際、一つ注意点としてトークンの上限が挙げられます。例えば、一時間の会議を文字起こしした場合、結果として得られる文章量はかなり多くなることが予想されます。gpt-3.5-turboの場合、上限トークンは約4,000です。一方、16kモデルでは約16,000トークンまでとなります。このトークンの上限を超えないように、対応策を実装することが必要です。

文章を分割させる

LangChainにはTextSplitterという文章を分割してlistにしてくれるツールがあります。
ある文字列を基準に分割してくれるのですが、デフォルトのものは一文字列しか指定出来ないので以下の方の方法を拝借しました。
https://www.sato-susumu.com/entry/2023/04/30/131338
以下のようにjapanese_spliterを定義します。

from typing import Any
from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter

class JapaneseCharacterTextSplitter(RecursiveCharacterTextSplitter):
    def __init__(self, **kwargs: Any):
        separators = ["\n\n", "\n", "。", "、", " ", ""]
        super().__init__(separators=separators, **kwargs)

japanese_spliter = JapaneseCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=0,
)

以下のようにすることでテキストの分割を出来ます!

texts = japanese_spliter.split_text(trans)

文字起こしを修正するプロンプト

音声文字起こしは一般的な言葉には対応可能ですが、社内用語や特定の技術用語などには対応が難しいことが多いです。そのため、修正の際のプロンプト設定が非常に重要となります。頻繁に使用されるキーワードや専門用語は、プロンプトに明示的に組み込むことで、より適切な修正が期待できます。

texts_modified=""
for text in texts:            
    prompt=f"##音声文字起こしで不自然な文を削除し、自然な文章に修正してください。\n##音声文字起こし\n{text}\n##修正した文章\n"
    messages=[{"role": "system", "content": "あなたは優秀な日本語のエディターです。"},
        {"role": "user", "content": prompt}
    ]
    text_modified = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=messages,
        temperature=0,
    )
    texts_modified=texts_modified+text_modified["choices"][0]["message"]["content"]

CharGPTのAPIについて詳細を知りたい方は以下のリンクで詳しく説明しています!
https://zenn.dev/tsuzukia/articles/df414098fcd5bd

最終コード

import openai
import streamlit as st
from typing import Any
from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter

class JapaneseCharacterTextSplitter(RecursiveCharacterTextSplitter):
    def __init__(self, **kwargs: Any):
        separators = ["\n\n", "\n", "。", "、", " ", ""]
        super().__init__(separators=separators, **kwargs)

japanese_spliter = JapaneseCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=0,
)
openai.api_key = 'your-api-key-here'
st.header('音声文字起こしアプリ')

upload_file = st.file_uploader('音声文字起こしするファイルを選択してください  \nAPIの上限により25MB以上のファイルは文字起こし不可です。\
                               ファイルを分割する等容量を少なくしてください', type=['m4a', 'mp3', 'webm', 'mp4', 'mpga', 'wav'])
if upload_file is not None:
    st.subheader('ファイル詳細')
    file_details = {'FileName': upload_file.name, 'FileType': upload_file.type, 'FileSize': upload_file.size}
    st.write(file_details)
    file_name=upload_file.name.split('.')[0]
    if upload_file.size > 25000000:
        st.error('エラー:ファイルが25MBを超えています。APIの上限により25MB以上のファイルは文字起こし不可です。ファイルを分割する等容量を少なくしてください', icon="🚨")
    trans_start=st.button('文字起こし開始')

    if trans_start:
        if upload_file is None:
            st.error('エラー:ファイルが選択されていません', icon="🚨")
        else:
            with st.spinner('***音声文字起こしを実行中です...***'):
                trans= openai.Audio.transcribe("whisper-1" ,upload_file)["text"]
            st.success('***音声文字起こしを完了しました***')
            st.write("***文字起こし結果***")
            st.write(trans)
            with st.spinner('***日本語の修正中です...***'):
                texts = japanese_spliter.split_text(trans)
                texts_modified=""
                for text in texts:            
                    prompt=f"##音声文字起こしで不自然な文を削除し、自然な文章に修正してください。\n##音声文字起こし\n{text}\n##修正した文章\n"
                    messages=[{"role": "system", "content": "あなたは優秀な日本語のエディターです。"},
                        {"role": "user", "content": prompt}
                    ]
                    text_modified = openai.ChatCompletion.create(
                        model="gpt-3.5-turbo",
                        messages=messages,
                        temperature=0,
                    )
                    texts_modified=texts_modified+text_modified["choices"][0]["message"]["content"]
            st.success('***日本語の修正を完了しました***')
            st.write("***文字起こし結果(修正後)***")
            st.write(texts_modified)

いかがでしたでしょうか?Streamlitを使用して、音声文字起こしアプリを実装するプロセスをご紹介しました。このアプリには日本語の修正機能も搭載しており、多岐にわたる用途での活用が期待できます。

現状においてはWhisperの日本語への対応能力は、英語と比較してまだ向上の余地があると感じられます。
今後の技術進歩とともに、このような課題も次第に改善されることを期待しています。それまでの間、Whisperをより適切に活用するための方法として、fine tuningや、大規模言語モデル(LLM)を用いた修正が有効です。

最後に、音声文字起こし技術の進化は日進月歩です!今回紹介した方法やツールも、定期的なアップデートや新たな技術の導入によって、更なる進化を遂げることでしょう😊

アプリの公開

今回のアプリをstreamlit shareで公開しています!ぜひ使ってみてください。
左のスライドバーにOpenAIのAPI Keyを設定してください。
https://app-app-dpubqpywksddidveswlc3x.streamlit.app/

また、これまで作成したアプリはgithubでも公開しています!ぜひチェックしてみてください。
https://github.com/tsuzukia21/streamlit-app

Discussion