1. 背景

2024年5月13日にchat-gpt4oがリリースされました。

ラジオ番組の文字起こしをしたく、whisperとchat-gpt4oを連携してやってみたら、思いの外良かったので記録します。

2. やり方

流れは以下のような感じです。

whisperで文字起こし → chat-gpt4oで校正

whisperで文字起こし

whisperは2パターンあります。1つはAPI使用する方法、もう1つはGitHubのコードを使って自宅で文字起こしする方法です。

両者を比較すると、smallだと文字起こしの精度はほぼ変わらないです。

コストでいうと、APIは1時間の音声で0.7ドルぐらいです。1ドル155円で計算すると、100円ちょっとです。ただ、自宅だと電気代はかかりますが、ほぼ無視していいぐらいの料金です。

また、自宅のGPUを使用できるので、文字起こしスピードは速いです。

上記から、文字起こしは自宅で実施します。

chat-gpt4oの校正

Whisperで出力した文字を校正します。

校正のスピードが速いし、料金も安いです。60分の文字起こしした文字数は2万字ぐらいで、だいたい0.3ドルぐらいです。

文字起こしと校正

校正前と校正後で比較します。

文意で改行してくれたりして、校正すごいです。

ピックアップすると下表のようになります。

校正前 校正後
国政選挙に気密日 国政選挙に官房機密費使用疑惑
可愛しの自宅を固く捜索した際 河井氏の自宅を捜索した際
中国新聞がスクープとして放置ました 中国新聞がスクープとして報じました。
疑いが不常 疑いが浮上
自民党の破罰によるパーティー券裏金事件 自民党の派閥によるパーティー券裏金事件

文字はある程度で区切る

以下のようにChat-GPTから「テキストは非常に長いため、一度に校正するのが難しいです。」と反応あり。ある程度短くしてテキストを渡す。

このように、文意を通しつつ文章全体を整えました。
申し訳ありませんが、提供いただいたテキストは非常に長いため、一度に校正するのが難しいです。いただいた分を一部ずつ校正させていただきますので、適宜追加してください。まず、最初の部分から始めます:

校正前(文字起こしした内容)

総理2800 スガッチ500に続き 国政選挙に気密日
中国新聞の記者たちが追った 自民党の裏金問題
2019年の参院選 広島選挙区をめくり
可愛勝雪元ホーム大臣が 地元の議員や公演会関係者ら100人に対し
現金をばらまいていたことが判明した 大規模買収事件
この事件では検察当局が 可愛しの自宅を固く捜索した際
当時の安倍政権の幹部4人から 現金合わせて6700万円を受け取った
疑いを示すメモが応集されていたことを 地元の中国新聞がスクープとして放置
ました メモには総理2800 スガッチ500 幹事長3300
あまり100などと書いてありこれらの資金が 買収に使われた疑いが不常
この報道は大きな話題となり 中国新聞の取材版は2023年度の
新聞労連ジャーナリズム対象を受賞しました その後も中国新聞はこの問題を追求
し続け他の選挙区でも現金が配られていた 実態や国政選挙に官房機密費が使われていた
疑惑などを報じています 自民党の破罰によるパーティー券裏金事件を
巡る政治と金の問題が国会でも議論される中 今夜はこの問題を取材し続ける
中国新聞記者の報告をお送りします では今夜のゲストご紹介します
スタジオにお越しいただきました 中国新聞社東京市社編集部記者の中川
まさはるさんですどうぞよろしくお願いいたします

校正後

総理2800 スガッチ500に続き 国政選挙に官房機密費使用疑惑

中国新聞の記者たちが追った 自民党の裏金問題。2019年の参院選、広島選挙区をめぐり、元法務大臣の河井克行氏が地元の議員や後援会関係者など約100人に現金をばらまいていたことが判明した大規模買収事件。この事件では、検察当局が河井氏の自宅を捜索した際、当時の安倍政権の幹部4人から現金合計6700万円を受け取った疑いを示すメモが押収されていたことを、中国新聞がスクープとして報じました。

メモには「総理2800」「スガッチ500」「幹事長3300」「あまり100」などと書かれており、これらの資金が買収に使われた疑いが浮上。この報道は大きな話題となり、中国新聞の取材班は2023年度の新聞労連ジャーナリズム大賞を受賞しました。その後も中国新聞はこの問題を追い続け、他の選挙区でも現金が配られていた実態や国政選挙に官房機密費が使われていた疑惑などを報じています。

自民党の派閥によるパーティー券裏金事件を巡る政治と金の問題が国会でも議論される中、今夜はこの問題を取材し続ける中国新聞記者の報告をお送りします。さて、今夜のゲストをご紹介します。スタジオにお越しいただきました、中国新聞社東京支社編集部記者の中川正治さんです。どうぞよろしくお願いいたします。

使ったコード

使用したコードは以下。

package main

import (
	"context"
	"fmt"
	"os"

	openai "github.com/sashabaranov/go-openai"
)

func main() {

	client := openai.NewClient("api-key")

	order := `whisperでラジオを書き起こしました。複数回に分割して書き起こし文書を伝えるので、文意が通るように校正してください。
	---	`

	// ファイルを読み込んでcontentに格納する
	content, err := os.ReadFile("2024-05-17_Session_time_1_free.txt")
	if err != nil {
		fmt.Printf("Failed to read file: %v\n", err)
		return
	}

	// 文字数が多いと校正しきれないと言われるので、文字を分割して渡す
	chunks := splitContent(string(content), 4500)

	for _, chunk := range chunks {
		resp, err := client.CreateChatCompletion(
			context.Background(),
			openai.ChatCompletionRequest{
				Model: openai.GPT4o20240513,
				Messages: []openai.ChatCompletionMessage{
					{
						Role:    openai.ChatMessageRoleUser,
						Content: order + chunk,
					},
				},
			},
		)

		if err != nil {
			fmt.Printf("ChatCompletion error: %v\n", err)
			return
		}
		// 出力結果をファイルに出力する
		fileName := "output.txt" // Replace "output.txt" with the actual file name
		file, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			fmt.Printf("Failed to open file: %v\n", err)
			return
		}
		defer file.Close()
		if _, err := file.WriteString(resp.Choices[0].Message.Content); err != nil {
			fmt.Printf("Failed to write file: %v\n", err)
			return
		}


	}

}

func splitContent(content string, chunkSize int) []string {
	var chunks []string
	for i := 0; i < len(content); i += chunkSize {
		end := i + chunkSize
		if end > len(content) {
			end = len(content)
		}
		chunks = append(chunks, content[i:end])
	}
	return chunks
}