James Tsang

James Tsang

A developer.
github
twitter
tg_channel

ARTS 打卡第 4 日

今日は 20 分遅れて打刻しました。残業と弟と長話をしたため、その後 LLM を使った TDD 開発に関する記事を選んで読んだのですが、内容が長すぎて当日の打刻を実現するために別の記事に変更しましたが、結局は時間オーバーになってしまいました。そして、質の高くない記事に変更してしまい、得られるものが少なかったです。ともあれ、まずはこの文章で打刻を完了し、LLM を使った TDD の記事を次の打刻記事の内容にすることにします。

A:263. 醜い数#

醜い数とは、素因数が 2、3、5 のみで構成される正の整数です。
整数 n が与えられたとき、n が醜い数かどうかを判断してください。もしそうであれば true を返し、そうでなければ false を返します。
例 1:
入力:n = 6
出力:true
説明:6 = 2 × 3
例 2:
入力:n = 1
出力:true
説明:1 には素因数がないため、全ての素因数は {2, 3, 5} の空集合と見なされ、通常は最初の醜い数と見なされます。
例 3:
入力:n = 14
出力:false
説明:14 は醜い数ではありません。なぜなら、別の素因数 7 を含んでいるからです。

比較的簡単な問題で、特に理論的なアプローチは思いつきませんでした。最初は 0 が醜い数ではないことを見落としていましたが、修正した後に提出しました:

function isUgly(n: number): boolean {
  if (n === 0) {
    return false
  }
  if (n === 1) {
    return true
  }
  if (n % 2 === 0) {
    return isUgly(n / 2)
  }
  if (n % 3 === 0) {
    return isUgly(n / 3)
  }
  if (n % 5 === 0) {
    return isUgly(n / 5)
  }
  return false
}

提出結果は:

1013/1013 ケースが通過しました (56 ms)
あなたの実行時間は 100% の TypeScript 提出を上回ります
あなたのメモリ使用量は 38.09% の TypeScript 提出を上回ります (44 MB)

ここで注意すべきは、私は再帰を使用して実装したということです。while (true)のループに変更することも可能です。

R:LangChain + Streamlit + Llama: 会話型 AI をあなたのローカルマシンに持ち込む#

皆さんは大規模言語モデルや LangChain に非常に慣れていると思うので、ここでは簡単に説明します。

大規模言語モデルは大きな注目を集めており、多くの開発者が大規模言語モデルを使用してチャットボットやパーソナルアシスタント、コンテンツ生成を行っています。大規模言語モデルの可能性は、開発者や AI、NLP コミュニティに大きな熱意をもたらしました。

特定の分野のデータを大規模言語モデルに注入することで、特に社内の文書知識ベースにおいて効率的にクエリ問題を解決することができます。この目的のために使用されるアーキテクチャは「検索強化生成」または「生成的質問応答」と呼ばれます。

LangChain とは何ですか?LangChain は、大規模言語 AI アプリケーションの構成要素を便利に連携させるための開発フレームワークで、開発者がチャットボットなどのアプリケーションを迅速に実現できるようにします。

この記事では、LangChain と LLaMA 7B モデルを使用して文書アシスタントを作成する方法について説明します (個人的には少し古く感じますが、現在は LLaMA2 です)

記事の情報構造:

  1. 仮想環境とファイル構造の作成
  2. ローカルで大規模言語モデルを取得
  3. 大規模言語モデルを LangChain に統合し、Prompt テンプレートをカスタマイズ
  4. 文書検索と回答生成
  5. Streamlit を使用してアプリを作成

1. 仮想環境とファイル構造の作成#

基本的なファイル構造と Python 仮想環境を作成します。主にモデルファイル、Notebook ファイル、app.py アプリケーションエントリファイルを作成します。著者のリポジトリをクローンできます:DocQA

2. ローカルで大規模言語モデルを取得#

LLaMA は Meta が発表した大規模言語モデルで、LLaMA2 は無料で商用利用できます。この記事では LLaMA1 を使用しています。HuggingFaceで LLaMA モデルを見つけ、bin ファイルを models ディレクトリにダウンロードするだけです。

GGML はオープンソースの C++ で書かれた機械学習テンソルライブラリで、量子化の方法を使用して消費者向けハードウェア上で LLM を実行できます。

では、量子化とは何ですか?LLM の重みは浮動小数点数であり、整数値よりもスペースと計算能力を多く占有します。量子化は、リソースの占有を減らすために重みの精度を減少させることです。GGML は 4 ビット、5 ビット、8 ビットの量子化をサポートしています。

メモリ、ハードディスク、およびモデルの効果を考慮して、モデルパラメータのサイズと量子化方法を選択する必要があります。サイズが大きく、量子化精度が高いほど効果が良くなりますが、リソースの占有も増えます。

GGML が C++ ライブラリであれば、Python でどのように使用するのでしょうか?これには llama-cpp-python プロジェクトが必要です。これは llama.cpp の Python バインディングで、Python を使用して LLaMA モデルを実行できるようにします。

これまでの説明は多かったですが、実行は非常に簡単で、数行の Python コードで完了します:

image

3. 大規模言語モデルを LangChain に統合し、Prompt テンプレートをカスタマイズ#

LLM にとって、その動作を簡略化して理解することは、入力テキストと出力テキストです。したがって、LangChain にとっても大部分の作業はテキスト中心です。

Prompt の微妙な違いが LLM の動作に大きな差を生むため、質の高い Prompt を生成する方法を考慮するために Prompt Engineering の概念が生まれました。LLM とのシームレスなインタラクションを実現するために、LangChain は Prompt テンプレート開発の機能を提供しています。通常、これには 2 つの部分が含まれます:テキストテンプレートと動的パラメータです。

簡単なアプリケーションでは、Prompt と入力パラメータを LLM に渡して結果を生成するだけで済みますが、複雑なアプリケーションでは一般的に LLM と他のコンポーネントを接続する必要があります。LangChain は接続の開発方法を提供しており、一連のコンポーネントを直列に呼び出すことができます。

4. 文書検索と回答生成#

多くの LLM アプリケーションでは、ユーザーが必要とするデータはモデルのトレーニングデータセットには含まれておらず、Prompt に外部から追加する必要があります。LangChain は、これらのデータをロード、変換、保存、クエリするために必要なコンポーネントを提供しています:

image

この 5 つのプロセスは:文書のロード - 文書の変換 - 埋め込み - ベクトルストレージ - ベクトル検索であり、以下は文書検索の全プロセスです:

image

このプロセスは比較的長いため、ここでは詳しく説明しませんが、これはローカルデプロイメントのソリューションであるため、埋め込みモデルもリモートサービスを使用せず、LLaMA モデルを使用して埋め込みを行う LangChain コンポーネントである LlamaCppEmbeddings を使用しています。

5. Streamlit を使用してアプリを作成#

著者は Streamlit について詳しく説明していませんが、これはメインの流れにとって比較的オプションのステップです。しかし、Streamlit を使用してファイルをアップロードする際、著者はメモリ不足を防ぐために、アップロードされたファイルを一時ディレクトリに raw.txt として保存していることを強調しています。現在は txt タイプのファイルのみがサポートされていますが、PDF や CSV のファイルをサポートするように変更することもできます。最後に Streamlit ライブラリを呼び出すことで、この LangChain ベースの LLM アプリケーションをウェブアプリケーションに変えることができました:

# 依存関係をインポート
import streamlit as st 
from langchain.llms import LlamaCpp
from langchain.embeddings import LlamaCppEmbeddings
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma


# レイアウトをカスタマイズ
st.set_page_config(page_title="DOCAI", page_icon="🤖", layout="wide", )     
st.markdown(f"""
            <style>
            .stApp {{background-image: url("https://images.unsplash.com/photo-1509537257950-20f875b03669?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1469&q=80"); 
                     background-attachment: fixed;
                     background-size: cover}}
         </style>
         """, unsafe_allow_html=True)

# アップロードされたファイルを一時的に書き込む関数
def write_text_file(content, file_path):
    try:
        with open(file_path, 'w') as file:
            file.write(content)
        return True
    except Exception as e:
        print(f"ファイルの書き込み中にエラーが発生しました: {e}")
        return False

# プロンプトテンプレートを設定
prompt_template = """以下の文脈を使用して、最後の質問に答えてください。答えがわからない場合は、わからないと言ってください。答えを作り上げようとしないでください。
{context}
質問: {question}
回答:"""
prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

# LLMと埋め込みを初期化
llm = LlamaCpp(model_path="./models/llama-7b.ggmlv3.q4_0.bin")
embeddings = LlamaCppEmbeddings(model_path="models/llama-7b.ggmlv3.q4_0.bin")
llm_chain = LLMChain(llm=llm, prompt=prompt)

st.title("📄 文書会話 🤖")
uploaded_file = st.file_uploader("記事をアップロード", type="txt")

if uploaded_file is not None:
    content = uploaded_file.read().decode('utf-8')
    # st.write(content)
    file_path = "temp/file.txt"
    write_text_file(content, file_path)   
    
    loader = TextLoader(file_path)
    docs = loader.load()    
    text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
    texts = text_splitter.split_documents(docs)
    db = Chroma.from_documents(texts, embeddings)    
    st.success("ファイルが正常に読み込まれました!!")
    
    # LLMを通じてクエリ    
    question = st.text_input("ファイルから何かを尋ねる", placeholder="テキスト内の....これ....に似たものを見つけて?", disabled=not uploaded_file,)    
    if question:
        similar_doc = db.similarity_search(question, k=1)
        context = similar_doc[0].page_content
        query_llm = LLMChain(llm=llm, prompt=prompt)
        response = query_llm.run({"context": context, "question": question})        
        st.write(response)

image

私は個人的に Streamlit のアプリケーションに関心があり、Gradio によるプロトタイプ開発の後、より複雑なアプリケーション開発には Streamlit の機会があると思っていますが、残念ながらこの記事では深く掘り下げられておらず、私が最も見たかった内容が簡単に流されてしまいました。

T:CoDeF#

動画から動画への LLM で、出力が安定しており、品質も良好です。

image

S:SQ3R 読書法#

SQ3R は 5 つの単語を表しています:Survey(調査)、Question(質問)、Read(読む)、Recite(復唱)、Review(復習)。学習の前に、まず内容をざっと調べ、その調査に基づいて自分の質問を提起します。これは何についてのもので、どのような問題を解決するのか。そして、その質問を持って深く読むことで、答えを見つけます。
最後に本を閉じて、自分で一度復唱します。この本は何についてのもので、私たちはどのような問題を持っていて、この本はどのように解決しているのか。最後に復習を行い、学習成果を強化します。この 5 つのステップを経て、この本の内容は本当に吸収されることができます。


Reference:

  1. ARTS 打刻活動
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。