paint-brush
Ruby で小さな言語モデル (TLM) を構築する方法: ステップバイステップ ガイド@davidesantangelo
520 測定値
520 測定値

Ruby で小さな言語モデル (TLM) を構築する方法: ステップバイステップ ガイド

Davide Santangelo10m2025/02/03
Read on Terminal Reader

長すぎる; 読むには

この記事では、Ruby を使用して非常にシンプルな言語モデルを作成する方法について説明します。入力テキストから「学習」し、観察したパターンに基づいて新しいテキストを生成する基本的なマルコフ連鎖モデルを構築します。
featured image - Ruby で小さな言語モデル (TLM) を構築する方法: ステップバイステップ ガイド
Davide Santangelo HackerNoon profile picture
0-item

この記事では、Ruby を使用して非常にシンプルな言語モデルを作成する方法を説明します。真の大規模言語モデル (LLM) には膨大な量のデータと計算リソースが必要ですが、言語モデリングの背後にあるコア概念の多くを示すおもちゃのモデルを作成できます。例では、入力テキストから「学習」し、観察したパターンに基づいて新しいテキストを生成する基本的なマルコフ連鎖モデルを構築します。


注:このチュートリアルは教育目的であり、言語モデリングへの簡略化されたアプローチを示しています。GPT-4 などの最新のディープラーニング LLM に代わるものではなく、基礎となるアイデアの紹介です。


目次

  1. 言語モデルの基礎を理解する
  2. Ruby環境の設定
  3. データ収集と前処理
  4. マルコフ連鎖モデルの構築
  5. モデルのトレーニング
  6. テキストの生成とテスト
  7. 結論

言語モデルの基礎を理解する

言語モデルは、単語のシーケンスに確率を割り当てるシステムです。本質的には、特定のシーケンスが特定のコンテキストで発生する可能性を学習することで、言語の統計的構造を捉えるように設計されています。つまり、モデルは大量のテキストを分析して、単語が通常どのように連続しているかを理解し、シーケンス内で次にどの単語またはフレーズが来るかを予測できるようにします。このような機能は、テキスト生成や自動補完などのタスクだけでなく、翻訳、要約、感情分析などのさまざまな自然言語処理 (NLP) アプリケーションでも中心的な役割を果たします。

GPT-4 などの現代の大規模言語モデル (LLM) は、ディープラーニング技術と膨大なデータセットを使用して、言語の複雑なパターンを捉えます。これらのモデルは、入力テキストを多数の人工ニューロン層で処理することで動作し、驚くほど流暢な人間のようなテキストを理解して生成することができます。ただし、これらの洗練されたシステムの背後には、学習した確率に基づいて単語のシーケンスを理解して予測するという同じ基本的な考え方があります。

言語をモデル化する最も簡単な方法の 1 つは、マルコフ連鎖を使用することです。マルコフ連鎖は、単語の出現確率はテキストの履歴全体ではなく、先行する単語の限られたセットのみに依存するという仮定に基づいて動作する統計モデルです。この概念はマルコフ特性として知られています。実際的には、このモデルは、シーケンス内の次の単語は最新の単語を見るだけで予測できると仮定しています。この単純化により、計算上は問題が扱いやすくなり、同時にデータ内の有用なパターンも捕捉できます。

マルコフ連鎖ベースの言語モデルでは:

  • 将来の状態 (次の単語) は、現在の状態 (前の単語) のみに依存します。つまり、最後のいくつかの単語 (モデルの順序によって決定) がわかれば、次に何が来るかを予測するのに十分なコンテキストが得られます。会話やテキストの履歴全体を考慮する必要がないため、複雑さが軽減されます。
  • 先行する単語を前提として、次にどの単語が来るかの確率分布を構築します。モデルはテキストのコーパスでトレーニングされるため、特定のシーケンスに続くさまざまな単語の可能性を学習します。この確率分布は、生成フェーズでシーケンス内の次の単語を選択するために使用されます。通常は、学習した確率を尊重するランダム サンプリング プロセスが使用されます。

私たちの実装では、設定可能な「順序」を使用して、予測を行う際に前の単語をいくつ考慮するかを決定します。順序が高いほどコンテキストが充実し、モデルが前の単語に関する情報をより多く持つため、より一貫性があり、文脈的に関連性の高いテキストになる可能性があります。逆に、順序が低いとランダム性が高くなり、予測しにくくなるものの、より創造的な単語のシーケンスにつながる可能性があります。一貫性と創造性の間のこのトレードオフは、言語モデリングにおける中心的な考慮事項です。

これらの基本原理を理解することで、マルコフ連鎖モデルの単純さと、より複雑なニューラル言語モデルの基礎となる基本的な考え方の両方を理解できるようになります。この拡張された視点は、言語予測の背後にある統計的メカニズムを理解するのに役立つだけでなく、自然言語処理のより高度な技術を試すための基盤も築きます。


Ruby環境の設定

始める前に、システムに Ruby がインストールされていることを確認してください。次のコマンドを実行して、Ruby のバージョンを確認できます。

 ruby -v

Ruby がインストールされていない場合は、 ruby-lang.orgからダウンロードできます。

このプロジェクトでは、専用のディレクトリとファイルを作成することをお勧めします。

 mkdir tiny_llm cd tiny_llm touch llm.rb

これで、Ruby コードを記述する準備が整いました。


データ収集と前処理

トレーニングデータの収集

言語モデルには、テキスト コーパスが必要です。トレーニングには任意のテキスト ファイルを使用できます。簡単な例では、次のような小さなテキスト サンプルを使用できます。

 sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT

データの前処理

トレーニングの前に、テキストを前処理しておくと便利です。

  • トークン化:テキストを単語に分割します。
  • 正規化:オプションでテキストを小文字に変換したり、句読点を削除したりします。

私たちの目的には、Ruby のString#splitメソッドがトークン化に十分に機能します。


マルコフ連鎖モデルの構築

モデルの動作をカプセル化するために、 MarkovChainという Ruby クラスを作成します。クラスには次のものが含まれます。

  • チェーンの順序(先行する単語の数)を設定する初期化子。
  • 入力テキストからチェーンを構築するtrainメソッド。
  • チェーンからサンプリングして新しいテキストを生成するgenerateメソッド。

以下はモデルの完全なコードです。

 class MarkovChain def initialize(order = 2) @order = order # The chain is a hash that maps a sequence of words (key) to an array of possible next words. @chain = Hash.new { |hash, key| hash[key] = [] } end # Train the model using the provided text. def train(text) # Optionally normalize the text (eg, downcase) processed_text = text.downcase.strip words = processed_text.split # Iterate over the words using sliding window technique. words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end # Generate new text using the Markov chain. def generate(max_words = 50, seed = nil) # Choose a random seed from the available keys if none is provided or if the seed is invalid. if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words # Form the key from the last 'order' words. key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? # Randomly choose the next word from the possibilities. next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end

コードの説明


  • **初期化:**コンストラクターのinitialize順序 (デフォルトは 2) を設定し、チェーンの空のハッシュを作成します。ハッシュにはデフォルトのブロックが与えられ、すべての新しいキーが空の配列として開始されます。


  • **モデルのトレーニング:** trainメソッドは、テキストの文字列を受け取り、それを正規化して単語に分割します。 each_consを使用して、長さorder + 1の連続した単語グループを作成します。最初のorder単語がキーとして機能し、最後の単語がそのキーの可能な継続の配列に追加されます。


  • **テキストの生成:** generateメソッドはシード キーから開始します。何も指定されていない場合は、ランダム キーが選択されます。次に、最大単語数に達するまで、最後のorder単語を検索し、次の単語をサンプリングすることで、シーケンスを繰り返し構築します。


モデルのトレーニング

MarkovChainクラスができたので、テキスト データでトレーニングしてみましょう。

 # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create a new MarkovChain instance with order 2 model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!"

上記のコードを実行すると(たとえば、 llm.rbに保存してruby llm.rbを実行する)、提供されたサンプルテキストを使用してモデルがトレーニングされます。


テキストの生成とテスト

モデルをトレーニングしたら、新しいテキストを生成できます。サンプル テキストを生成して印刷するコードを追加してみましょう。

 # Generate new text using the trained model. generated_text = model.generate(50) puts "Generated Text:" puts generated_text

テキスト生成のシードを提供することもできます。たとえば、モデル内のキーの 1 つ ( "once upon"など) がわかっている場合は、次のようにします。

 seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

さまざまなシードとパラメータ (順序や単語の最大数など) を試してみると、出力がどのように変化するかを確認できます。


完全な例: 小さな LLM のトレーニングとテスト

上記のすべての手順を組み合わせた完全な Ruby スクリプトは次のとおりです。

 #!/usr/bin/env ruby # llm.rb # Define the MarkovChain class class MarkovChain def initialize(order = 2) @order = order @chain = Hash.new { |hash, key| hash[key] = [] } end def train(text) processed_text = text.downcase.strip words = processed_text.split words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end def generate(max_words = 50, seed = nil) if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create and train the model model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!" # Generate text without a seed generated_text = model.generate(50) puts "\nGenerated Text:" puts generated_text # Generate text with a specific seed seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

スクリプトの実行

  1. スクリプトをllm.rbとして保存します。
  2. ターミナルを開き、 llm.rb含むディレクトリに移動します。
  3. 次を使用してスクリプトを実行します。
 ruby llm.rb

モデルがトレーニングされたことを示す出力と、生成されたテキストの例が 2 つ表示されます。


ベンチマーク

次の表は、Tiny LLM 実装のさまざまなバージョンのベンチマーク メトリックをまとめたものです。各メトリックについては以下で説明します。

  • モデル:言語モデルの名前またはバージョン識別子。
  • 順序:次の単語を予測するためにマルコフ連鎖で使用される前の単語の数。通常、順序が高いほど、使用されるコンテキストが多くなり、一貫性が高まる可能性があります。
  • トレーニング時間 (ミリ秒):提供されたテキスト データでモデルをトレーニングするのにかかるおおよその時間 (ミリ秒単位で測定)。
  • 生成時間 (ミリ秒):サンプル テキスト出力を生成するのに必要な時間 (ミリ秒単位)。
  • メモリ使用量 (MB):トレーニングおよび生成中にモデルによって消費されるメモリの量。
  • 一貫性評価:生成されたテキストがどの程度一貫性があり、文脈的に適切であるかを示す主観的な評価 (5 点満点)。

以下はベンチマーク データを含むマークダウン テーブルです。

モデル

注文

トレーニング時間 (ミリ秒)

生成時間 (ミリ秒)

メモリ使用量 (MB)

一貫性評価

小さな LLM v1

2

50

10

10

3/5

小さな LLM v2

3

70

15

12

3.5/5

小さな LLM v3

4

100

20

15

4/5

これらのベンチマークは、さまざまなモデル構成間のトレードオフの概要を簡単に示します。次数が増えると、モデルのトレーニングとテキスト生成にかかる時間がわずかに長くなり、メモリ使用量も増える傾向があります。ただし、リソース消費量の増加は、生成されるテキストの一貫性の向上を伴うことがよくあります。

結論

このチュートリアルでは、Ruby を使用して非常にシンプルな言語モデルを作成する方法を示しました。マルコフ連鎖技術を活用して、次のようなシステムを構築しました。

  • 単語の遷移を学習してサンプルテキストをトレーニングします
  • 学習したパターンに基づいて新しいテキストを生成します

このおもちゃのモデルは、実稼働レベルの LLM とは程遠いものですが、言語モデルが基本的なレベルでどのように機能するかを理解するための足がかりとなります。より高度なテクニックを取り入れたり、句読点をより適切に処理したり、Ruby を機械学習ライブラリと統合してより洗練されたモデルにしたりすることで、このアイデアを拡張できます。

楽しいコーディングを!