時代に取り残されないために、今更ながらChatGPTのAPIで遊んでみた記録
あいさつ
こんにちは、AIソリューション開発部の森です。 主業務としては、CRAIS for Textで使用されている効果予測器の開発・改善に携わっています。 CRAIS for Textについては、過去の記事で紹介されていますので、気になる方はぜひこちらも合わせて一読ください。
遊びに至るまで
ここ最近抱えていた、OpenAIが提供するChatGPTのAPIを使ってみたいという願望が出発点でした。
業務上、ChatGPTのAPIを直接使うわけではありません。しかし、業務内外を問わず、ChatGPTのAPIについての話についていけないと感じることが多くなり、時代に取り残されつつあることに危機感を抱いていました。加えて、AIを使う側のロールプレイング体験がしたかったというのがあります。業務では、AIモデルの学習・開発は行っていますが、AIモデルを使って何かをする機会はありません。 ご存知の通り、OpenAIが提供するChatGPTのAPIは、自分でモデルをデプロイすることなく、使うことを体験できることを可能にしてくれました。
にもかかわらず、今までは、自分の怠慢でChatGPTのAPIを触る時間を取ってませんでした。 この度、担当プロジェクトの業務の合間を縫って、ChatGPTのAPIで遊んでみることにしました。
今更感が満載ですが、お付き合いいただけると幸いです。
考え、遊ぶ
どう遊ぶかを考える
ChatGPTのAPIで遊ぶことに決めましたが、それだけでは遊びの範囲が狭まると思い至りました。ChatGPTの特性上、豊富なテキストデータがあれば、もっと楽しめると思いました。 さらに、テキストデータへアクセスできるAPIを利用することで、さらに多くのテキストデータで遊ぶことができることに気づきました。 いろいろ考えたのですが、今回は無料で提供されているarXivのAPIを選びました。ariXvのAPIを通じて、膨大な数の論文データにアクセスできます。
arXivとは、科学研究論文や学術論文を無料で公開・アクセスできるwebサイトです。例えば、研究者が自分の研究成果を査読前に公開し、他の研究者と共有するために使われています。
以上の経緯で、ChatGPTのAPIを使って、Pythonを介して論文のテキストデータを対象に楽しむことにしました。
検索で遊んでみる
さて、手元に論文を用意するために、検索で遊んでみます。 arXivのAPIを使って論文をとるには、以下のURLにHTTPリクエストを送る必要があります。 たとえば、Deep Learningという文言を含む論文を1件取ってくる場合は、以下のようになります。
http://export.arxiv.org/api/query?search_query=Deep+Learning&start=0&max_results=1
このAPIを通じて、ChatGPTのAPIなしで、論文を取ってくることは可能です。しかしながら、キーワードが思いつかない場合や、ほかの論文と似たような論文を探したい場合は、キーワードを探す手間がかかります。 少なくとも自分は、それがめちゃくちゃめんどくさいと感じます。 ということで、ChatGPTのAPIを使って、ネットの文章やPDFデータからキーワードを抽出し、そのキーワードを使って論文を検索することにしました。
上記の考えをもとに、コードを書いてみました。試しに、Large Language Modelの一つであるLLaMAに関連する論文を取ってみることにしました。
def search_papers(user_input, input_pdf=None, search_papers_amount = 10): # ユーザー入力からキーワードを抽出 keywords = [] if user_input: # ユーザー入力が日本語の場合、英語に翻訳する # 日本語キーワードでのリクエストはエラーになるため user_input = check_and_translate_japanese(user_input) keywords = extract_keywords(user_input) # ユーザー入力が空でPDFがアップロードされている場合、PDFからキーワードを抽出 if input_pdf: pdf_text = read_pdf(input_pdf) # PDFが日本語で書かれている場合、トークン制限エラーを防ぐために小さなチャンクサイズを使用 chunk_size_for_keywords = 25000 if re.search(r'[ぁ-んァ-ン]', pdf_text[:1000]): chunk_size_for_keywords //= 2 # PDFテキストからキーワードを抽出(トークン制限エラーを防ぐためにテキストをチェック) keywords.extend(extract_keywords(pdf_text[:chunk_size_for_keywords], keyword_limit=3, gpt_model="gpt-3.5-turbo-16k")) # キーワードが抽出されている場合、論文を検索。それ以外の場合、空のリストを返す papers = [] if len(keywords) > 0: # ChatGPT APIによって抽出されたキーワードを使用してarxivから論文を検索 search_query = parse_keywords_into_search_query(keywords) url = f'http://export.arxiv.org/api/query?search_query={search_query}' \ f'&start=0&max_results={search_papers_amount}' \ f'&sortBy=relevance' \ f'&sortOrder=descending' data = urllib.request.urlopen(url) data = data.read().decode('utf-8') # XMLデータを辞書にパース papers = parse_search_results_into_dict(data) return papers if __name__ == "__main__": result = search_papers( user_input="私は大規模言語モデルのLLaMAに関する論文が読みたいです。", input_pdf=None, search_papers_amount=1 )
arXivのAPIは、論文の検索結果をXML形式で返してくれます。今回は、そのXMLをパースして、論文の情報を辞書型で保存しています。 上記のコードでは、検索で取ってきた論文は、以下のようになっています。 このデータ構造を参照することで、諸々の情報を取ってこれます。
[{'abstract': ' We release Code Llama, a family of large language models for ' ..., 'authors': ['Baptiste Rozière', ...], 'category': ['cs.CL'], 'comments': [], 'id': 'http://arxiv.org/abs/2308.12950v2', 'pdf_link': 'http://arxiv.org/pdf/2308.12950v2', 'published': '2023/8/24', 'summary': '', 'title': 'Code Llama: Open Foundation Models for Code', 'updated': '2023/8/25'}]
要約で遊んでみる
論文を手に入れることができたので、何をするかを考えます。基本的に、論文はめちゃくちゃ長いので、要約してみることにしました。 しかしながら、論文全体の要約はAbstractという形で全ての論文に記載されているので、論文の特定のポイントを要約させることにしました。 たとえば、その論文の手法についてのみ要約するとか、その論文の実験結果についてのみ要約するとかですね。 実際に、コードを書いてみる前に、目を背けることができない問題が頭に浮かびました。それは、以下の2つです。
要約のフォーマットが不確実
ChatGPTは、文章を生成することができます。しかし、フォーマットがばらばらになってしまう可能性があります。
箇条書きになったり、箇条書きにならなかったり、文章だけに変わったり、といったことが起こります。これでは、可読性が低くなってしまいます。
今回は、フォーマットを決めて、ChatGPTにそれに従って要約してもらうことにしました。トークン制限エラーが発生
ただ、長い論文をそのままChatGPTに投げると、トークン制限エラーが発生してしまう可能性が高いです。
論文を小さな塊に分けて、それぞれの塊について要約することにしました。
塊に分けると別の問題が発生します。それは、チャンクによって、情報のばらつきが発生することです。
例えば、手法についてのみ要約する場合、論文の最初の塊には手法についての情報がない可能性があります。
対処法は実にシンプルです。プロンプトに、塊に特定のポイントに対応する箇所がなければ、ないと返してもらうことにしました。その上で、すべての塊について要約して、一番新しい要約を返してもらうようにしました。
以下のコードが、上記を踏まえて、書いたコードです。 早速、さきほど取ってきた論文の「論文で提案された手法」を要約してもらうことにしました。
def summarize_target_info_in_paper(pdf_file, target_information, output_language): summary = "" if pdf_file is None and target_information is None or target_information == "": Warning("対象情報が指定されていません。") return summary # ローカルからPDFを読み込む paper_text = read_pdf(pdf_file) # PDFが日本語で書かれている場合、トークン制限エラーを防ぐために小さなチャンクサイズを使用 chunk_size = 30000 if re.search(r'[ぁ-んァ-ン]', paper_text[:1000]): chunk_size //= 2 # トークン制限エラーを防ぐためにテキストをチャンクに分割 pdf_text_chunks = chunk_text(paper_text, chunk_size=30000) # 論文内の目的の情報を要約する if len(pdf_text_chunks) > 0: summary_format_prompt = "#Format\n #One sentence summary\n #Detail\n #Key Points\n\n" command_prompt ="# Procedure of Your Job\n" \ f"1. find information related to '{target_information} in the paper (#Paper). Otherwise, Please return 'No information is found.' and stop the task." \ f"2. summarize all the portions of the academic paper (#Paper) that focus only on '{target_information}' in English." \ f"3. format the summary as follows:\n" \ for text_chunk in pdf_text_chunks: # プロンプトの部分を一つのプロンプトに組み合わせる prompt_paper_chunk = f"# 論文\n {text_chunk}" prompt = f"{prompt_paper_chunk}{command_prompt}{summary_format_prompt}" # ChatGPTに論文内の目的の情報を要約してもらう response = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", organization=openai.organization, messages=[ {"role": "user", "content": prompt}, ] ) # 応答が "No information is found." でない場合のみ、summary変数が更新される if response.choices[0].message.content != "No information is found.": summary = response.choices[0].message.content # 要約を出力言語に翻訳 if summary != "" and output_language != "English": summary = translate_by_gpt(summary, "English", output_language) return summary if __name__ == "__main__": # 論文をdata/pdfsにダウンロード download_paper_pdfs(result) # 論文を要約 by ChatGPT API summary_result = summarize_target_info_in_paper( pdf_file="pdfs/Code_Llama_Open_Foundation_Models_for_Code.pdf", # path to pdf file target_information="詳しく論文で提案された手法を説明してもらえますか?", # target information to summarize output_language="Japanese" # output language of the summarization ) print(summary_result)
それでは、実行して、要約の結果を見てみましょう。
要約: この論文では、コード生成のための大規模な言語モデルであるCode Llamaについて説明しています。Code Llamaはオープンモデルの中でも最先端のパフォーマンスを提供し、インフィリング能力、大きな入力コンテキスト、プログラミングタスクの命令に従う能力をサポートしています。 詳細: この論文では、コード生成のために設計された大規模な言語モデルであるCode Llamaのファミリーについて紹介しています。これらのモデルはLlama 2をベースにしており、他のオープンモデルと比べて改良されたパフォーマンスを提供しています。Code Llamaには、基礎モデル、Pythonの特化モデル、命令に従うモデルなど、さまざまなアプリケーションをカバーする複数のフレーバーが含まれています。 Code Llamaモデルは16kトークンのシーケンスで訓練され、最大100kトークンの入力を処理することができます。これらのモデルは周囲のコンテンツに基づいたインフィリングをサポートし、いくつかのコードベンチマークで改善が見られ、HumanEvalとMBPPでそれぞれ53%と55%のスコアを達成しています。Code Llama - Python 7BモデルはHumanEvalとMBPPでLlama 2 70Bを上回り、すべてのモデルはMultiPL-Eで他の公開モデルを上回っています。 論文ではまた、Code Llamaモデルのトレーニングプロセスについても説明しています。まず、一般的なテキストとコードのデータに基づいて事前学習されたLlama 2を使用して基礎モデルのコードトレーニングが行われます。その後、多目的の目的関数でファインチューニングを行い、インフィリング能力を可能にし、RoPE位置埋め込みのファインチューニングによって長い入力コンテキストを処理するために訓練されます。さらに、より具体的なプログラミングの指示を提供することで、モデルの有用性と安全性を向上させるために命令のファインチューニングが適用されます。 キーポイント: 1. Code Llamaはコード生成のための大規模な言語モデルのファミリーです。 2. これらのモデルは一般的なテキストとコードのデータの組み合わせで訓練されます。 3. Code Llamaは、コードベンチマークにおいてオープンモデルの中でも最先端のパフォーマンスを達成しています。 4. これらのモデルは周囲のコンテンツに基づいたインフィリングをサポートし、大きな入力コンテキストを処理することができます。 5. 命令のファインチューニングにより、モデルの有用性と安全性が向上します。
要約の結果を一見すると、手法については説明してくれています。 手法の中身とは少しそれた内容も入っています。例えば、「いくつかのコードベンチマークで改善が見られ、HumanEvalとMBPPでそれぞれ53%と55%のスコアを達成しています」の部分は、手法についての説明ではないですね。 プロンプトについては、さらに工夫が必要そうです。
遊んでみて
さて、今更感満載ではありますが、ChatGPTのAPIで軽く遊んでみました。 ChatGPTのAPIアルゴリズム・仕組みに対する理解はありましたが、APIとして使ったことがありませんでした。 今回は、AIを使うことに対する理解を深め、流行りに着いていくことができた良い機会でした。
本記事では、ChatGPTのAPIで遊ぶ最初の一歩として、純粋なChatGPTのAPIだけを使って遊んでみました。 LLaMA IndexやLangChainなど、ChatGPTのAPIを使っていろんな事ができるようなAPIもあります。さらに、直近、GPT-4 VのAPIも公開されました。次は、さらに進んでそれらで遊んでみるのもありかなと思っています。
Opt Technologies ではエンジニアを募集中です。カジュアル面談も可能ですので、下記リンク先よりお気軽にご応募ください。