2025,江端さんの技術メモ

変更した箇所を教えて貰いました。

変更した箇所

[Dockerfile]

  • FROM pgrouting/pgrouting:12-3.0-master → 15-3.5-main
  • RUN apt install -y libpqxx-6.2 → libpqxx-6.4

[docker-compose.yml]

  • version をコメントアウト。
  • services.osm2pgsql.image:

openfirmware/osm2pgsql:latest → iboates/osm2pgsql:latest

2025,江端さんの技術メモ

modelX.pickle というファイルには何が書き込まれていますか

modelX.pickle というファイルは、Pythonのpickleモジュールを使って保存された「オブジェクトの直列化ファイル(バイナリ形式)」です。
つまり「Pythonの変数・オブジェクトをそのままファイルに書き出したもの」であり、ファイルの中身をテキストで読んでも意味は分かりません。


🔍 一般的に書き込まれている内容

modelX.pickle という名前から推測すると、多くの場合は 機械学習モデルの学習済みパラメータ が保存されています。例えば:

  • scikit-learn の分類器や回帰モデル(例:RandomForestClassifier, LinearRegression

  • LightGBM / XGBoost の学習済みモデル

  • Pythonで定義した独自クラスのインスタンス

  • データ前処理用のオブジェクト(StandardScaler, PCAなど)


🧠 実際に何が入っているかを確認する方法

Pythonで次のようにして中身を確認できます。

import pickle

with open('modelX.pickle', 'rb') as f:
    model = pickle.load(f)

print(type(model))
print(model)

上記を実行すると:

  • type(model) でオブジェクトの型(例:sklearn.ensemble.RandomForestClassifier

  • print(model) でその概要(ハイパーパラメータなど)が分かります。


⚠️ 注意点

  • 安全性: 不明なpickleファイルを pickle.load() するのは危険です。悪意あるコードが実行される可能性があります。信頼できるソースのファイルのみ読み込んでください。

  • 互換性: モデルを保存したPythonやライブラリのバージョンが異なると、読み込みに失敗する場合があります。

「機械学習モデルの学習済みパラメータ」で正解だと思います。 これを前提として、pickleに記載されている内容を簡単な事例を用いて簡単に説明して下さい

はい、正解です。modelX.pickle のようなファイルには、たとえば 機械学習モデル(学習済みパラメータ) が保存されています。
以下では、とても簡単な例 を使って、その「中に何が記録されているのか」をわかりやすく説明します。


🎯 例:単純な線形回帰モデル

たとえば、次のようなPythonコードでモデルを作ったとします。

from sklearn.linear_model import LinearRegression
import pickle

# 学習データ(xとyの関係: y = 2x + 1)
X = [[1], [2], [3]]
y = [3, 5, 7]

# モデルを学習
model = LinearRegression()
model.fit(X, y)

# モデルを保存
with open('modelX.pickle', 'wb') as f:
    pickle.dump(model, f)

📦 modelX.pickle の中身(概念的なイメージ)

このとき modelX.pickle に保存されるのは、Pythonオブジェクトとしての学習済みモデル です。
テキストではなくバイナリ形式で次のような情報が詰まっています:

内容 説明
モデルのクラス名 sklearn.linear_model._base.LinearRegression
学習済みの係数 [2.0] (「y = 2x + 1」の 2 の部分)
切片(バイアス) 1.0 (「y = 2x + 1」の 1 の部分)
学習時に使用した内部設定 fit_intercept=True, copy_X=True などの設定情報
依存関係のメタデータ scikit-learnのバージョンなど

💡 つまり何が入っているのか

pickleファイルには、

「このモデルは、こういう種類のアルゴリズムで作られていて、こういう値を学習した」

という完全なオブジェクト構造と数値データが入っています。
そのため、後で次のようにして再利用できます。

with open('modelX.pickle', 'rb') as f:
    model = pickle.load(f)

print(model.coef_)   # [2.]
print(model.intercept_)  # 1.0
print(model.predict([[4]]))  # → [9.]

🧠 まとめ

  • pickleはPythonオブジェクトをそのまま保存する方法

  • modelX.pickleには、**学習済みのパラメータ(係数、構造、設定など)**が含まれている。

  • テキストではなくバイナリ形式なので、人間が直接読むことはできない。

  • 読み込むと、すぐに再利用できる学習済みモデルオブジェクトとして復元できる。

LightGBMの学習済みモデル、とした場合に、pickleに記載されている内容を簡単な事例を用いて簡単に説明して下さい

良い質問です。
今度は LightGBM の学習済みモデルpickle で保存した場合の「中に何が記録されているか」を、できるだけシンプルな例で説明します。


🎯 例:LightGBMで単純なモデルを作る

import lightgbm as lgb
import pickle

# 学習データ(y = 2x + ノイズ)
X = [[1], [2], [3], [4]]
y = [2.1, 4.0, 6.1, 8.0]

# モデルの学習
train = lgb.Dataset(X, label=y)
model = lgb.train({'objective': 'regression'}, train, num_boost_round=3)

# pickleで保存
with open('modelX.pickle', 'wb') as f:
    pickle.dump(model, f)

📦 modelX.pickle に書き込まれる内容(概念的なイメージ)

pickle には、LightGBM Booster オブジェクトが丸ごと保存されます。
その内部には、LightGBM が自動生成した 決定木(勾配ブースティング木) の情報がすべて含まれています。

内容 説明
モデル構造 どのような木構造が作られたか(ノード・枝・リーフ)
分割条件 各ノードで「x ≤ 2.5 なら左へ」といった条件式
リーフ値 予測値を出すためのリーフごとの出力(回帰なら数値、分類なら確率)
学習パラメータ objective='regression', num_boost_round=3 など
特徴量名・順序 学習時に使われた特徴量のリスト
メタ情報 LightGBM バージョン、学習日時など
学習履歴(optional) 学習時の損失関数の変化など(record_evalを使った場合)

🧠 pickleに含まれるもののイメージ(概念図)

LightGBM Booster
 ├── params
 │    ├── objective: 'regression'
 │    ├── num_boost_round: 3
 │    └── learning_rate: 0.1
 ├── trees
 │    ├── tree_0
 │    │    ├── split: feature_0 <= 2.5
 │    │    ├── left_leaf_value: 1.9
 │    │    └── right_leaf_value: 7.9
 │    ├── tree_1
 │    └── tree_2
 └── feature_names: ['feature_0']

🔍 復元するとこうなる

with open('modelX.pickle', 'rb') as f:
model = pickle.load(f)
# モデル情報
print(model.params)
# {'objective': 'regression'}# 木構造の文字列表示
print(model.dump_model())

⚠️ 補足:LightGBM専用の保存方法との違い

LightGBMにはもともと model.save_model('model.txt') という独自フォーマットがあります。
pickleとの違いは次の通りです。

保存方法 形式 主な用途
pickle.dump() Python専用のバイナリ形式 Python内で再利用(再学習や推論)
model.save_model('model.txt') テキスト形式(人が読める) 外部システムやC++ APIでの利用

💬 まとめ

LightGBM の pickle ファイルには:

「どんな木を作ったか」「どんな条件で分割したか」「各リーフでどんな値を出すか」
といった学習済みモデルのすべての構造情報とパラメータが、
Pythonオブジェクトとしてバイナリ形式で保存されている。

LightGBMを簡単に説明して下さい

LightGBM(ライトジービーエム)は、マイクロソフトが開発した機械学習ライブラリで、特に大規模データを高速に処理できる決定木ベースのアルゴリズムです。
正式名称は Light Gradient Boosting Machine です。


🌳 基本の考え方

LightGBM は 「勾配ブースティング(Gradient Boosting)」 という手法を使っています。
これは、たくさんの「弱い学習器(小さな決定木)」を順番に組み合わせて、最終的に高精度な予測モデルを作る方法です。

たとえば:

  1. まず1本目の木を作る(単純な予測器)

  2. その誤差を分析して、2本目の木で補正

  3. さらに残りの誤差を3本目の木で補正
    → これを何百本も繰り返して精度を上げる


⚙️ 特徴(他のライブラリとの違い)

特徴 説明
高速 大規模データでも学習が非常に速い(ヒストグラムベースの学習)
高精度 XGBoostなどと同等以上の精度を出せることが多い
メモリ効率が高い データを分位点でまとめることで、使用メモリを削減
カテゴリ変数に対応 one-hotエンコーディング不要でそのまま扱える
GPU対応 GPUを使ってさらに高速に学習可能

🧠 使いどころ

LightGBMは、以下のようなタスクでよく使われます。

  • 売上予測、需要予測(回帰)

  • 顧客の離脱予測(分類)

  • クレジットスコアリング(分類)

  • クリック率予測(CTR)

  • 機械の故障検知(異常検知)


🧩 イメージ図(単純化)

データ → 木① → 木② → 木③ → ... → 予測結果
(誤差を補う木を次々と追加)

💬 一言でまとめると

LightGBMは、「速くて賢い木の寄せ集め」で予測を行う機械学習ライブラリです。
特徴は「高速」「高精度」「大規模データに強い」。


希望があれば、具体的な学習例(数行のPythonコード) でLightGBMの動きを説明することもできます。
作成しましょうか?

作成して下さい

はい。では、LightGBMを最も簡単に理解できる最小限の例を示します。
わずか数行で、データの学習 → 予測 → 結果確認 までを体験できます。

import lightgbm as lgb
import numpy as np

# ---- 1. ダミーデータ作成 ----
# 入力データ (x) と正解データ (y = 2x + 1)
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([3, 5, 7, 9, 11])

# ---- 2. LightGBM のデータセット化 ----
train_data = lgb.Dataset(X, label=y)

# ---- 3. モデルの学習 ----
params = {
    'objective': 'regression',  # 回帰問題
    'metric': 'l2',             # 評価指標: 二乗誤差
}
model = lgb.train(params, train_data, num_boost_round=10)

# ---- 4. 予測 ----
y_pred = model.predict([[6]])
print("x=6 のときの予測値:", y_pred[0])

💡 実行結果(概念的な出力)

x=6 のときの予測値: 13.0

LightGBM は「y = 2x + 1」という関係を自動的に学習して、
x=6 のときに 13付近 の予測を返します。


🔍 何が起こっているか(ざっくり説明)

ステップ 内容
データ(x, y)をLightGBMが理解できる形式に変換
objective='regression' に基づいて木を1本ずつ構築
各木が「残差(誤差)」を少しずつ補正していく
10本ほどの木を積み重ねて、最終的に近似関数を得る
その結果、未知のxに対して予測が可能になる
⚙️ 保存と読み込み(pickleを使う場合)
import pickle

# 保存
with open('modelX.pickle', 'wb') as f:
    pickle.dump(model, f)

# 復元
with open('modelX.pickle', 'rb') as f:
    loaded_model = pickle.load(f)

print(loaded_model.predict([[10]]))  # → 約21.0

🎯 まとめ

  • LightGBMは木の集合で学習する高速な勾配ブースティング法

  • この例ではわずか10行で「y = 2x + 1」を再現

  • pickleで保存すれば、あとで再利用可能

2025,江端さんの技術メモ

私は、Posgresql+PostGISのDBをGo言語で作成した自前のMASシミュレータで動かしていますが、MATSimの方が高速でしょうか。
特にMATSimであったとしても最短距離計算(ダイクストラ等)をシミュレータで実施する必要があります。
前者のシステムはよく分かっていますが、MATSimのnetwork.xmlを取りあつかったことがないため、助言頂きたいです

結論から書きます。
「経路探索(ダイクストラ等)を自前MAS内で高頻度に回す」「静的~準静的コスト(free‐speed 近傍)で十分」「Goで実装資産がある」なら、MATSimへ全面移行しても経路探索単体の速度優位は保証されません。MATSimは反復型(replanning)交通シミュレーション全体のスループット最適化に主眼があり、経路探索も多スレッド化やALT(A* with Landmarks)などの実装はありますが、単発の最短路クエリ性能だけを競う設計ではありません。従って、現行のGo実装に以下を加える「ハイブリッド」が実務的です。すなわち、OSM→MATSim network.xml 生成だけをMATSim/Contrib-OSMに任せ、ネットワークはMATSim互換形式で統一しつつ、経路探索はGo側で最適化する、という方針です。

以下、判断材料と具体策です。

1) 速度・スケーラビリティの比較観点

MATSimが有利になるのは、反復計算(多数エージェント×複数iteration)を並列で回し続けるケース、かつ時間帯依存のトラベルタイムやリンク容量制約を織り込み、ルート再計算を大量に発生させる場合です。内部の QSim がイベント駆動・並列処理に最適化され、ルーティングもスレッドプールで回せるため、シミュレータ全体のスループットは高くなりやすい。一方で「最短距離(あるいは最短時間)を高速に繰り返し答える」こと自体は、Go側でダイクストラ/A*にALT(Landmarks)多段グリッド前処理Contraction Hierarchies等を実装・導入した方が速くできます(MATSim本体はCHを標準では持ちません)。

要するに、探索単体のピーク性能=Go側が有利/シミュレーション全体の反復運用=MATSimが有利という整理になります。

2) 「MATSimのnetwork.xmlだけ取り込む」運用

MATSimの network.xml は、ノード集合と有向リンク集合の単純明快な表現です。単位は距離[m]、速度[m/s]、容量[veh/h]、車線数[integer]が基本です。最短時間コストは典型的に length / freespeed、最短距離は length をそのまま重みとします。CRSは config.global.coordinateSystem に依存するため、生成時点でメートル系(UTM等)に変換されていることが重要です。

スキーマ(最小限)

<network>
  <nodes>
    <node id="n1" x="..." y="..."/>
    ...
  </nodes>
  <links capperiod="01:00:00">
    <link id="l1" from="nA" to="nB"
          length="123.4"  freespeed="13.9"
          capacity="1800.0"  permlanes="1.0"
          modes="car"/>
    ...
  </links>
</network>

運用ポイントは、(1) 片方向リンクの明示(fromto)、(2) 自動車以外のモードを入れるなら modes を分ける、(3) 渋滞等を扱わない静的探索なら freespeedlength で十分、の三点です。

3) Goで network.xml を読む最小実装のイメージ

encoding/xml で十分です。読み込んで隣接リスト(片方向)を組み立て、重みを「距離」「所要時間」いずれかに切り替えれば、そのままダイクストラ/A*に渡せます。

package matsimnet

import (
	"encoding/xml"
	"os"
)

type Network struct {
	XMLName xml.Name `xml:"network"`
	Nodes   Nodes    `xml:"nodes"`
	Links   Links    `xml:"links"`
}
type Nodes struct {
	List []Node `xml:"node"`
}
type Node struct {
	ID string  `xml:"id,attr"`
	X  float64 `xml:"x,attr"`
	Y  float64 `xml:"y,attr"`
}
type Links struct {
	List []Link `xml:"link"`
}
type Link struct {
	ID        string  `xml:"id,attr"`
	From      string  `xml:"from,attr"`
	To        string  `xml:"to,attr"`
	Length    float64 `xml:"length,attr"`     // [m]
	FreeSpeed float64 `xml:"freespeed,attr"`  // [m/s]
	Capacity  float64 `xml:"capacity,attr"`   // [veh/h]
	PermLanes float64 `xml:"permlanes,attr"`
	Modes     string  `xml:"modes,attr"`      // 例 "car"
}

func LoadNetwork(path string) (*Network, error) {
	f, err := os.Open(path)
	if err != nil { return nil, err }
	defer f.Close()
	var net Network
	if err := xml.NewDecoder(f).Decode(&net); err != nil {
		return nil, err
	}
	return &net, nil
}

この後は、nodeID -> int を付番して配列・スライスに詰め替え、リンクを有向エッジとして adj[u] = append(adj[u], edge{v, w}) に積みます。重み w は最短距離なら link.Length、最短時間なら link.Length / link.FreeSpeed を用いれば良い。ヒープを使ったダイクストラ実装、あるいはユークリッド距離の下界を使ったA*をGoで実装すれば、現行システムに自然に統合できます。静的ネットで多クエリなら、ALT(Landmarks)の事前計算(数十~数百の基準点)を入れると探索ノード数が激減します。

4) 実務判断の指針

  1. 既存のGoベースMASが堅牢で、主たるボトルネックが「経路探索単体の速度」なら、MATSimへ置換するよりグラフ前処理(ALT/CH)+並列化+メモ化をGo側に導入する方が投資対効果が高い。

  2. 一方で、需要変動・混雑の内生化、反復的な行動再計画(モード/出発時刻/経路)まで踏み込み、交通工学的整合性(容量・ファンダメンタル図・信号等)を広く扱いたいなら、MATSimのシナリオ全体に寄せる価値が大きい。

  3. 中庸案として、OSM→MATSim network.xml 変換だけ利用し、ネットワーク仕様を国際的に通用するフォーマットへ整えつつ、シミュレータ本体はGoで維持するのが現実的。

5) 最低限の検証チェックリスト

  • network.xml のCRSがメートル系であること(長さ・速度の整合)。

  • 需要側(起終点)の座標も同一CRSに揃えること。

  • 「双方向通行」と思っている道路が、実データでは一方向リンクの対として表現されている点に注意(逆方向リンク欠落の確認)。

  • 速度未設定リンク(freespeed)がないかの点検(OSM変換時のデフォルト適用)。


結語。
江端さんの前提(Goで自前MAS、PostGIS理解が深い、最短経路を高速に回したい)に対しては、**MATSimは「ネットワーク標準化と将来拡張の土台」*として採用し、探索エンジンは引き続きGoで高度化、という構成が最も筋が良いと考えます。必要であれば、network.xml→隣接リスト化→ダイクストラ/ A(ALT)まで、実運用に耐えるGo実装の最小雛形を提示します。

私のやってきたMASの仕組みや目的についてご存知であるという前提で質問します。
現在のGo言語ベースのシミュレータをMATSimに移行するメリットはありますか?

 

結論(江端さん前提での率直評価)
現行Go製MASの主目的が “空間上の接触機会の定量化(共時空間/共移動)を高速に回すこと” で、経路探索は静的~準静的(free-speed 近傍)中心なら、全面移行の純粋なスピード/生産性メリットは限定的。移行より “必要部だけMATSimを借りる” ハイブリッドの方が合理的」です。

MATSimに「移す価値が出る」条件(価値)

  • 反復的Replanning(出発時刻/経路/モード変更)を大規模・並列で回し、混雑内生化(容量・速度低下・遅延波及)を扱いたい。

  • PT統合(GTFS→pt2matsim)や複数モード(歩行/自転車/自動車/バス)を1つの行動モデル上で整合させたい。

  • 活動ベース(Activity-based)需要やスコアリングによる行動選好のキャリブレーションを活用したい。

  • 既存エコシステム(イベントログ、可視化、評価ツール群)を研究フレームに素早く取り込みたい。

「移しても旨味が薄い」条件(江端さんの今)

  • 主眼:共時空間/RCMの定量で、経路は「静的最短時間/距離」で十分、探索単体の高速化がKPI。

  • 既にGo + PostGISで資産・知見・検証データが豊富(実運用の可観測性・可制御性が高い)。

  • 経路探索はALT/CH/並列化などグラフ前処理の導入余地が大きく、Goでさらに速くできる

  • 試験設計上、渋滞の内生化や行動再計画がスコープ外(論文要件上も混ぜたくない)。

現実解(推奨アーキテクチャ)

  • ハイブリッド案

    1. OSM→MATSimのnetwork.xmlだけ生成(道路属性の標準化・CRS統一・品質担保)。

    2. network.xmlをGoで読み、隣接リスト+静的コストに落として、ALT/CH等で探索を最適化。

    3. 需要(起終点)も同一CRSに統一し、共時空間の算出・可視化は従来のGo系パイプラインを維持。

  • メリット:データ仕様が国際標準に寄る/将来、必要になったら一部だけMATSim機能を漸進導入できる(例:PT、混雑内生化、イベントログ解析)。

もし「MATSimに寄せる」なら得られる追加価値

  • 容量制約+速度低下の内生化 → RCMの時間密度プロファイルが実交通に近づく可能性。

  • Replanningで政策差(バス路線/運賃/高齢化施策)の行動変化まで一貫検証できる。

  • イベントログ標準化(travelTime/queue/vehicle等)で、後処理・可視化の再利用性が上がる。

移行コスト/リスク(簡潔)

  • 学習曲線(Config/QSim/Scoring/Contrib依存の理解)。

  • 既存Goの検証資産の移植負担(数値一致の担保)。

  • 実験設計の変質(混雑や行動再計画を入れると、論文の前提がブレる恐れ)。

今すぐの実務アクション(最小コスト順)

  1. 現行フロー維持のまま、MATSimでnetwork.xmlのみ生成(CRS=UTM、速度/車線のデフォルト表を固定)。

  2. Go側でnetwork.xml読込 → 最短“時間/距離”切替実装を共通化。

  3. 探索高速化:ALT(Landmarks)→並列バッチ→CHの順に導入して効果測定。

  4. 余力が出たら、**pt2matsim(GTFS)**でPTネットを別ブランチに試験統合(RCM比較の将来拡張)。


要約

  • 速度KPI中心・静的探索中心の今の研究段階では、全面移行のリターンは小

  • **“ネットワーク標準化(MATSim)×探索最適化(Go)”**の二段構えが最も筋が良い。

  • 将来、政策比較や混雑内生化まで踏み込む段で、段階的にMATSim機能を取り込むのが安全です。

 

2020/07,江端さんの技術メモ

	m_conn->prepare(                    // HACK 1. 2文を1文に(PQExec仕様)、2. JOIN周り、 3. ORDER BYを消せないか?
		"find_shortest_path_by_dijkstra_calc",
		R"(
			INSERT INTO s_dijkstra_memoization (source, target, node, edge, length_m)
			
				SELECT
					$1, $2, d.node, d.edge, coalesce(ways.length_m, 0) AS length_m
				FROM
					pgr_dijkstra (
						'SELECT gid AS id, source, target, length_m AS cost from ways',
						start_vid:=$1::bigint, end_vid:=$2::bigint, directed:=false::boolean
					) as d LEFT JOIN ways ON ways.gid = d.edge
			
			WHERE NOT EXISTS (SELECT source, target FROM s_dijkstra_memoization WHERE source = $1 AND target = $2);
		)",
					//RETURNING node, edge, length_m;
		2
	);

のところで、問題が発生しているのは分かるのだけど、私にとっては上記な呪文であり、さっぱり分からない。

とりあえず、psql から地道に部分的に動かしてみて、問題点を探っている。

ca_sim=# SELECT gid, source, target, length_m AS cost from ways;

gid | source | target | cost
------+--------+--------+--------------------
1 | 55 | 23 | 121.17749638577058
2 | 844 | 1 | 710.1279290280786
3 | 4807 | 2 | 40.26044319993349
4 | 6296 | 3 | 251.21876036796243
5 | 22 | 4 | 2181.7785758382133
6 | 18 | 6 | 435.2439693722031
7 | 4304 | 6 | 950.7269653866767
8 | 6289 | 7 | 29.068747479235597

となるが、

ca_sim=# SELECT * FROM pgr_dijkstra(
'SELECT gid, source, target, length_m AS cost FROM ways',
2, 3
);
ERROR: Column 'id' not Found
CONTEXT: SQL function "pgr_dijkstra" statement 1

となる。pgr_dijkstraを実行する為には、パラメータの名前まで注意しなければなならないらしい。

ca_sim=# SELECT * FROM pgr_dijkstra(
'SELECT gid AS id, source, target, length_m AS cost FROM ways',
2, 3
);
seq | path_seq | node | edge | cost | agg_cost
-----+----------+------+------+--------------------+--------------------
1 | 1 | 2 | 342 | 224.26753559377073 | 0
2 | 2 | 229 | 8773 | 295.36268951709405 | 224.26753559377073
3 | 3 | 6359 | 8771 | 41.04204131422657 | 519.6302251108648
4 | 4 | 6357 | 344 | 136.12897946273213 | 560.6722664250914
5 | 5 | 231 | 8688 | 58.55503032553214 | 696.8012458878235
6 | 6 | 6294 | 8690 | 191.73693713817062 | 755.3562762133556
7 | 7 | 6296 | 4 | 251.21876036796243 | 947.0932133515262
8 | 8 | 3 | -1 | 0 | 1198.3119737194886
(8 rows)

くるしまぎれに、

start_vid:=$1::bigint, end_vid:=$2

の部分に、

1、2

を、実数でベタ書きしたら、エラーが取れて、シミュレーションが走りだした(原因不明)

しかし、

start_vid:=$1::bigint, end_vid:=$2 → $1, $2

では、

ERROR: function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7: pgr_dijkstra(
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.

Error:ERROR: function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7: pgr_dijkstra(
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.

となってしまう。


ブログを読んで頂いたからから、以下のアドバイスを頂いたので、その観点より挑戦。

上の箇所の$1や$2は、SQLインジェクションされないためのprepare文のプレースホルダーです。
ドキュメントの該当箇所は次のものです。
https://www.postgresql.org/docs/current/sql-prepare.html
$1と$2に該当するものをうしろから代入してあげれば動く(はず)と思います。
MySQLは、たしか「?」がプレースホルダで、SQLiteも「?」がプレースホルダ(https://docs.python.org/ja/3/library/sqlite3.html をみたら、合ってました)だったように覚えています。PostgreSQLは$+数字をつかうことになっています。

そこで、このページを印刷しながら、比較して、色々試してみた

                pgr_dijkstra(
                    (SELECT gid AS id, source, target, length_m AS cost from ways),
                    start_vid:=$1::bigint, end_vid:=$2::bigint, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge

ERROR:  subquery must return only one column
LINE 8:       (SELECT gid AS id, source, target, length_m AS cost fr...
              ^

Error:ERROR:  subquery must return only one column
LINE 8:       (SELECT gid AS id, source, target, length_m AS cost fr...
            FROM
                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    (start_vid:=$1::bigint), (end_vid:=$2::bigint), directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge
ERROR:  syntax error at or near ":="
LINE 9:       (start_vid:=$1::bigint), (end_vid:=$2::bigint), direct...
                        ^

Error:ERROR:  syntax error at or near ":="
LINE 9:       (start_vid:=$1::bigint), (end_vid:=$2::bigint), direct...

すでに試してみたが、これもやってみた

                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    $1, $2, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge
ERROR:  function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7:      pgr_dijkstra(
             ^
HINT:  Could not choose a best candidate function. You might need to add explicit type casts.

Error:ERROR:  function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7:      pgr_dijkstra(
             ^
HINT:  Could not choose a best candidate function. You might need to add explicit type casts.

なんかエラーが増えてきたようだ(unknownが3つになった)。

他の行と比較してみながら、start_vid と end_vid を消してみた(これが記載されていない別の行があったので)

            FROM
                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    $1::bigint, $2::bigint, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge

これで動いた。

けど、理由はさっぱり分かりません。$1::bigint, $2::bigint がキャストの書式?

トライアンド&エラーでやろうとすると、こういう目にあう、という良い教訓として下さい。

2025,江端さんの技術メモ

はい、Mouse without Borders はホストを含めて最大 4台のPC まで接続して動作します。
したがって、今お使いのホストPCに加えてあと2台、合計3台のマシンを同時に制御することが可能です。

  • ホストPC … 実際にマウス・キーボードが接続されているPC

  • クライアントPC … 追加で操作するPC(最大3台)

設定画面で各PCのセキュリティコードコンピュータ名を入力すれば、3台構成でも問題なく利用できます。

もし希望があれば、3台目を追加する際の具体的な接続手順も説明できます。

中央

右側

左側

現在3台のマシンがあります。

左側の設定画面です。
問題点は 
(1)中央のマシンのマウスは、中央と右側を動き
(2)右側のマシンのマウスも、中央と右側を動き
(3)左側のマシンのマウスは、左側と中央を動き

3台共通して動かないということです。対処方法を教えてください

画像3枚を確認しました。状況を見ると、Mouse without Borders の「接続」が中央と片側のPCだけで完結していて、3台を一つのマトリックスとして認識できていません。
これだと、中央マシン経由の2台運用はできても、左 ↔ 右 の直接移動ができない状態になります。


原因

  • 全てのマシンが同じ「Security Key」と同じネットワーク上にある必要がありますが、現在はそれぞれの接続が部分的にしかリンクしていません。

  • 3台を同一の「Computer Matrix」に登録する設定が完了していません。
    (現状は、中央–右、中央–左のペア接続になっている)


対処手順(全台で共通設定にする)

  1. Security Key を統一

    • 左・中央・右、すべてのマシンで「Shared encryption key」の値を同じにします。

    • 今回の画像を見ると左と中央は tS3_hJ5-jK7_fD9/、右は非表示ですが違っている可能性があるため、手入力で合わせます。

    • 入力後「Apply」を押す。

  2. 全マシンを互いに認識させる

    • 各マシンの設定画面で、右下の「Go through the setup experience」をクリック。

    • 表示された「This is the new computer」に、ホスト(マウス・キーボードを使う中心PC)のSecurity Keyとコンピュータ名を入力。

    • これを、全てのマシン間で行い、3台すべてのサムネイルが表示される状態にする。

  3. Computer Matrix の並びを調整

    • 設定画面で3台のサムネイルをドラッグして、物理的な配置(左・中央・右)と同じ並びにする。

    • 「Two Row」のチェックは不要なら外す。

    • 「Apply」を押して保存。

  4. テスト

    • いずれかのマシンからマウスを動かし、左右両方向に移動できることを確認。

    • 全台でキーボード入力も共有されるか確認。


💡 ポイント

  • Mouse without Borders は全員が同じ Security Key を共有し、全台が同じ「Computer Matrix」に表示されている状態でないと、完全な3台運用になりません。

  • 途中のマシン経由でしか動かせない場合は、片方のPCがもう一方を認識していない証拠です。

2025,江端さんの技術メモ

pybind11とは何ですか

pybind11 とは、C++で書かれたコードをPythonから簡単に呼び出せるようにするライブラリです。具体的には、C++関数・クラスをPythonにバインディング(橋渡し)するための軽量なヘッダオンリーライブラリです。


✅ 要点まとめ

項目 内容
名称 pybind11
用途 C++コードをPythonから使えるようにする(バインディング)
特徴 ヘッダオンリー、軽量、C++11以降対応
主な代替 Boost.Python、SWIG など
主な用途 Pythonから高速なC++シミュレータや計算ライブラリを使うときなど

🧠 なぜ使うのか?

Pythonは書きやすいが遅い、C++は速いけど扱いにくい。
そのため、重い処理はC++で書き、インターフェースだけPythonで操作したいことがよくあります。

text
たとえば:
Python部分 → 学習ロジック(強化学習など)
C++部分 → シミュレータの高速実行

この「橋渡し」に pybind11 が使われます。


🔧 使用イメージ

C++側(example.cpp)

#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.def("add", &add, "A function which adds two numbers");
}

Python側

import example
print(example.add(1, 2))  # → 3

このように、C++の関数をまるでPythonで書いた関数のように使えるようになります。



🔚 まとめ

観点 内容
概要 C++とPythonをつなぐためのヘッダオンリーライブラリ
主目的 C++で書いた高速なロジックをPythonから呼び出す
利点 軽量・高速・簡単・CMakeと相性良好
使用場面 シミュレーション、機械学習、画像処理、ゲームエンジン等のバックエンド連携

必要であれば、あなたの railway_simulator モジュールが pybind11 で作られているかどうかも確認する手順を案内できます。

使用イメージを実際に動かすにはどうすれば良いですか

以下に、pybind11 を使って C++ 関数を Python から呼び出せるようにする最小構成の実行手順をまとめます。
Linux / WSL / Mac / MSYS2 などの UNIX互換環境を前提にしています(Windows でも MSYS2 や WSL を使えば同じです)。


✅ ゴール

C++ の関数を Python から呼び出せるようにする:

cpp
int add(int a, int b) { return a + b; }

↓ Pythonから:

python
>>> import example
>>> example.add(1, 2)
3

🔧 準備

① Python と pybind11 のインストール

以下をターミナルで実行:

bash
pip install pybind11

(C++コンパイラが必要です:gcc, clang, cl など)


📁 ② ファイルを作成

example.cpp(C++側)

#include <pybind11/pybind11.h>
int add(int i, int j) {
    return i + j;
}
PYBIND11_MODULE(example, m) {
    m.def("add", &add, "A function which adds two numbers");
}

⚙️ ③ ビルド(共有ライブラリ作成)

以下のコマンドをそのままコピー&ペースト:

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    `python3 -m pybind11 --includes` \
    example.cpp -o example`python3-config --extension-suffix`

このコマンドは:

  • pybind11 のヘッダを含めて

  • example.so(もしくは .pyd)というPython拡張モジュールを作成します


✅ ④ 実行(Pythonから呼び出し)

ターミナルで Python を起動し、次を試してください:

>>> import example
>>> example.add(10, 5)
15

成功すれば pybind11 のバインディング成功です!


📎 注意点

項目 内容
ビルドは1行で 改行してしまうと command not found になります。1行にしてください
Windows の場合 -fPIC は不要/MSYS2環境を推奨します
pybind11 の場所 python3 -m pybind11 --includes により自動取得できます
Python バージョン python3.11 など環境によって異なる場合があります。必要なら python3-config を変更してください

✅ もしうまくいかなかったら…

次の情報を貼ってもらえれば、個別にビルドコマンドを調整できます:

  • OS種別(Windows / Mac / Ubuntu / WSL など)

  • Pythonバージョン(python3 --version

  • コンパイラの種類(gcc --version など)


必要であれば、CMakeLists.txt を使った構成や、.pyx ファイルとの違い(Cython との比較)についてもご説明します。

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    python3 -m pybind11 --includes \
    example.cpp -o examplepython3-config --extension-suffix
> > /home/ebata/.pyenv/versions/3.8.10/bin/python3: No module named pybind11
example.cpp:1:10: fatal error: pybind11/pybind11.h: No such file or directory
    1 | #include <pybind11/pybind11.h>
      |          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.

と言われました

ありがとうございます。エラーメッセージを解析すると、以下の2つの問題が発生しています:


❌ 問題1:pybind11 がインストールされていない

bash
/home/ebata/.pyenv/versions/3.8.10/bin/python3: No module named pybind11

これは pybind11Python環境に未インストールであることを示しています。


❌ 問題2:C++ヘッダファイル pybind11/pybind11.h が見つからない

bash
example.cpp:1:10: fatal error: pybind11/pybind11.h: No such file or directory

これは Python モジュールが見つからなかったため、--includes で得られるインクルードパスも空になり、結果として pybind11.h が見つからないという流れです。


✅ 解決手順

以下の手順に従って修正してください。


① pybind11 を正しい Python 環境にインストール

あなたは pyenv を使って Python 3.8.10 を使っているようなので、次のコマンドを使います:

bash
~/.pyenv/versions/3.8.10/bin/pip install pybind11

※または pyenv の環境をアクティベートしてから:

bash
pyenv activate 3.8.10
pip install pybind11

② 再度ビルドコマンドを実行

インストール後、以下の1行コマンドを再度実行してください:

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    `python3 -m pybind11 --includes` \
    example.cpp -o example`python3-config --extension-suffix`

(必要なら python3~/.pyenv/versions/3.8.10/bin/python3 に書き換えてください)


✅ うまくいけば…

カレントディレクトリに example.cpython-38-x86_64-linux-gnu.so のようなファイルができます。

Pythonで次のように実行できれば成功です:

bash
python3
>>> import example
>>> example.add(2, 3)
5

📌 備考(トラブル防止)

内容 対処法
複数バージョンの Python がある 明示的に ~/.pyenv/versions/3.8.10/bin/python3 を使う
pip install したのに ModuleNotFoundError pippython が同じ環境を見ているか確認(例:which pip, which python3
pybind11 のヘッダが見つからない python3 -m pybind11 --includes の出力結果を確認して -I/path/to/pybind11/include を手動で追加

2025,江端さんの技術メモ

以下に、Go言語で実装するシンプルな mTLS(相互TLS認証)通信プログラムを示します。
これは、サーバがクライアント証明書の検証を行い、クライアントがサーバに接続してデータを送るという最小構成のデモです。


✅ ファイル構成

.
├── server.go ← mTLS対応サーバ
├── client.go ← mTLS対応クライアント
├── ca.crt ← ルートCA証明書
├── server.crt ← サーバ証明書(caで署名)
├── server.key ← サーバ秘密鍵
├── client.crt ← クライアント証明書(caで署名)
└── client.key ← クライアント秘密鍵

🖥️ サーバ側(server.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS構成
	tlsConfig := &tls.Config{
		ClientAuth: tls.RequireAndVerifyClientCert,
		ClientCAs:  caPool,
		MinVersion: tls.VersionTLS12,
	}

	server := &http.Server{
		Addr:      ":8443",
		TLSConfig: tlsConfig,
	}

	http.HandleFunc("/secure", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "mTLS通信成功!")
	})

	log.Println("mTLSサーバ起動中:https://localhost:8443/secure")
	err = server.ListenAndServeTLS("server.crt", "server.key")
	if err != nil {
		log.Fatal(err)
	}
}

💻 クライアント側(client.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書と鍵の読み込み
	cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
	if err != nil {
		log.Fatal(err)
	}

	// サーバ証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS設定
	tlsConfig := &tls.Config{
		Certificates:       []tls.Certificate{cert},
		RootCAs:            caPool,
		InsecureSkipVerify: false,
	}

	client := &http.Client{
		Transport: &http.Transport{TLSClientConfig: tlsConfig},
	}

	// サーバにリクエスト
	resp, err := client.Get("https://localhost:8443/secure")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Printf("サーバ応答: %s\n", string(body))
}

🔐 証明書の作成(OpenSSL例)

# CA作成
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=TestCA" -days 3650 -out ca.crt

# サーバ証明書
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650

# クライアント証明書
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=myclient" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650

✅ 実行方法

  1. 上記のOpenSSLコマンドで証明書類を生成
  2. server.go を起動

  3. client.go を別ターミナルで起動

以下のエラーが発生

ebata@DESKTOP-1QS7OI7:~/test_tls$ go run client.go
2025/07/29 02:33:59 Get "https://localhost:8443/secure": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead

x509: certificate relies on legacy Common Name field, use SANs instead
→ Go の TLS ライブラリ(特に Go 1.15 以降)では、Common Name(CN)だけの証明書ではサーバ認証に失敗します。
現在は SAN(Subject Alternative Name) が必要です。


✅ 対応策:証明書を SAN を含めて再生成する

OpenSSL の設定を使って、SAN付き証明書を発行します。


🔧 手順(openssl

1. san.cnf という設定ファイルを作成:

# san.cnf
[req]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext
prompt             = no

[req_distinguished_name]
CN = localhost

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

2. サーバ証明書を SAN付きで再生成:

# サーバ鍵生成
openssl genrsa -out server.key 2048

# CSR作成
openssl req -new -key server.key -out server.csr -config san.cnf

# 証明書発行(CAで署名)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 3650 -extensions req_ext -extfile san.cnf

✅ 説明

項目 内容
CN Common Name。旧来のFQDN指定(非推奨)
SAN Subject Alternative Name。現在はこちらが必須
localhost クライアントから接続するホスト名と一致させる必要あり

GoはこのSANにlocalhostが含まれていないとTLS検証に失敗します。


✅ 再実行の流れ(修正後)

go run server.go
# → 起動確認「mTLSサーバ起動中:https://localhost:8443/secure」

go run client.go
# → サーバ応答: mTLS通信成功!

2025,江端さんの技術メモ

C:\の128GBが限界に達していて、このままではWindow updateもできないので、C:\128GのSSDを500GのSSDに交換することにしました。

で、業者に問い合わせしてみたら、SSD抜きの作業で1万4000円~という連絡がきまして、「ちょっと高いなぁ」と思い、自力での交換に挑戦してみました。

今回購入したのは、この2つ。

(クリックするとamazonに飛びます)

業者に頼むより1万円ほど安いですが、リストア失敗を考えると1万円なら業者に頼んでもよかったかもしれません。実際、PCの中を明けて、どこに部品があるのか探し回ることになりました。

そんでもって、それをリストアするのにもひと苦労しました。

今回は、業者に預けている時間(2~3日くらい)の方が問題でした。私は、明日も、このPCで作業したかったので。

------

最近は、マニュアル読まなくなりました。でも、こういう動画があれば、マニュアルより安心して作業できます。

一発目のシステムの再起動が上手くいかず、青冷めましたが、その後、安定して起動するようになり一安心です(このドキドキが嫌いです。1万円ならペイする不安感です)。

で、最後は、ChatGPTの登場です。

画像をアップロードしました

ディスク1のクローンを作成したのですが、C:\にはあいからわず108.91GBしかありません。346.52GBの未割り当て領域をC:\に加えて拡張したいです。手順を教えて下さい

「ディスクの管理」画面を見る限り、ディスク1上のCドライブ(108.91GB)に続けて未割り当て領域(346.52GB)が存在しているため、以下の手順で Cドライブを拡張 できます。


✅ 手順:Windows標準機能「ディスクの管理」でCドライブを拡張する方法

  1. 「ディスクの管理」を開いた状態のまま、Cドライブ(108.91 GB)を右クリック

  2. 「ボリュームの拡張(E)...」を選択

  3. 「ボリューム拡張ウィザード」が表示されるので、「次へ(N)」をクリック

  4. 「使用可能な最大サイズ」が既に入力されていることを確認して「次へ(N)」

  5. 「完了(F)」をクリック


⚠ 注意点

  • 上記は「Cドライブの直後に未割り当て領域がある場合」に限って有効です。

  • もし他のパーティションがC:と未割り当て領域の間にある場合は、この方法は使えません(サードパーティ製ソフトが必要になります)。


💡 補足

  • 操作後、Cドライブは 108.91 + 346.52 ≒ 455.43GB に拡張されます。

  • システムドライブの操作なので、念のため事前にバックアップを取っておくと安全です。

これで、明日は安心して開発環境のインストールができます。

-----

なんやかんやで、また1万円ほど使ってしまいましたが、こういう作業も含めて、『これが私の趣味みたいなものかなぁ』と、半ば諦めています。

あまり、共感得られにくい趣味です ーー 引きこもりなら「ゲーム」方がウケは言いだろうな、と思います。

学業や業務の為に「システムのリストア」をしている"引きこもり"は、理解を得られにくいように思います。

 

2025,江端さんの技術メモ

WSLでWebサーバ(テスト)を立ち上げる方法

前提: wslにはUbuntuが入っていて、emacsなどもすでに入っている

(1)スタートメニューで Ubuntu を検索 → 起動すれば、確実にUbuntuが立ち上がります。

(2)デフォルトディレクトリは、/home/ebata で、Windowsからこの場所を見るには、\\wsl$\Ubuntu\home\ebata にアクセス(エクスプローラー)

(3)make my_web_project して、cd my_web_projectして、emacsで、index.htmlを作る。
こんな感じindex.htmlを作っておく。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>WSL Webサーバ テストページ</title>
</head>
<body>
  <h1>ようこそ!</h1>
  <p>これは WSL 上で動作している Web サーバから配信されたページです。</p>
  <p>このページは <code>index.html</code> です。</p>
</body>
</html>

(4)ebata@DESKTOP-1QS7OI7:~/my_web_project$ python3 -m http.server 8888 でサーバを立ち上げる

(5)これで、ローカルから、http://localhost:8888/ でアクセスできる。

(6)ところが、リモートから、http://192.168.0.3:8888/ ではアクセスできない

(7)そこで、方法①(推奨)Windows側でポートフォワーディングを設定
Windows PowerShell を 管理者として実行して以下を実行:
netsh interface portproxy add v4tov4 listenport=8888 listenaddress=0.0.0.0 connectport=8888 connectaddress=127.0.0.1

さらに Windowsファイアウォールでポート 8888 を許可してください:
New-NetFirewallRule -DisplayName "AllowPort8888" -Direction Inbound -LocalPort 8888 -Protocol TCP -Action Allow

(8)これで、リモートから、http://192.168.0.3:8888/ ではアクセスできるようになる

2025,江端さんの技術メモ

取り敢えず、192.168.0.8  の c:\Users\tomoi\rag_projectの中身でやったこと一覧

Ollamaは、どうやって知識を蓄えることができますか?

Ollama自体(正確にはOllama上で動くLLM)は「知識を蓄える(=記憶を永続化する)」機能は原則持っていません。理由と補足は以下の通りです:


🔍 なぜOllama(+LLM)は知識を蓄えないのか?

  • Ollamaが実行するのは事前学習済みの言語モデル(例:LLaMA 3やMistral)であり、内部に知識を書き込む(=学習させ直す)機能はない

  • 一時的な文脈(会話履歴など)は保持できるが、Ollama再起動や会話のリセットで消える。


✅ 知識を“蓄える”ように使いたい場合の方法

方法1: ベクトルDB+RAGで「覚えさせる」

RAG (Retrieval-Augmented Generation) という手法を使うと、ローカルLLMでも“知識を覚えたように使う”ことができます。

構成例:

要素 内容
LLM Ollama + llama3/mistral
知識DB ベクトルDB(例えば Chroma, Weaviate, FAISS など)
統合フレームワーク LangChain, LlamaIndex など
処理内容 PDF, テキスト, Web等 → 分割 → ベクトル化 → 質問に応じて該当知識を検索 → LLMに渡す

📌 つまり、「知識」を蓄えるのはLLMではなく、外部のベクトルデータベースです。


方法2: チューニング(学習)で知識そのものを埋め込む

これは本当の意味での「知識の蓄積」になりますが、難易度が高くなります。

方法

方法名 内容 難易度
LoRA(Low-Rank Adaptation) 特定の知識をモデルに微調整として埋め込む 中〜上
Full fine-tuning モデル全体を再学習 非現実的(高コスト・GPU必須)

🧠「覚えているように見せる」簡易テク

  • LangChainやLlamaIndexで、テキストやPDFファイルを渡すだけで“知識として答える”ように見せられる。

  • これにより、対話履歴を記憶しているように錯覚させることも可能です。


現在、Ollamaが動いていますが、知識を“蓄える”ようにしたいですが、その前にRAGとチューニングの違いについて教えて下さい

はい、非常に重要な質問です。
以下に「RAG」と「チューニング(LoRAなど)」の違いと使い分けを、わかりやすく解説します。


🧠 RAG(Retrieval-Augmented Generation)とは?

▶ 特徴

  • LLMの外部に知識を持たせる。

  • 質問に応じて必要な情報を検索してLLMに与える。

  • モデル自体は一切変更しない。

▶ イメージ

LLMが「図書館で調べながら答えてくれる」ような使い方

▶ メリット

  • 高速かつ安全(モデル改変なし)

  • データの更新が簡単(PDF差し替えなど)

  • ローカルでも構築しやすい

▶ デメリット

  • 検索精度に依存する(ベクトル検索や分割方法が重要)

  • 外部検索結果に基づくため、「推論能力」に制限あり


🔧 チューニング(Fine-tuning / LoRA)とは?

▶ 特徴

  • LLMのパラメータ自体を再学習する。

  • モデル内部に知識を埋め込む。

  • LoRA(Low-Rank Adaptation)を使うと軽量に実現可能。

▶ イメージ

LLMが「訓練して記憶を更新した頭脳を持っている」ような使い方

▶ メリット

  • 対話スタイルやドメイン知識を完全に内面化できる

  • オフラインでも即答できる(検索なし)

▶ デメリット

  • 訓練が難しい(GPU・スクリプト・パラメータ設計)

  • データの入れ替えが容易でない(再チューニングが必要)

  • 学習エラーで性能が落ちることもある


🔄 RAG vs チューニング 比較表

比較項目 RAG チューニング(LoRA含む)
知識の格納先 外部DB(検索) LLM本体(学習)
導入の簡単さ ◎ 簡単 △ 難易度高
データ更新 ◎ 簡単(ファイル差替え) ✕ 毎回再学習が必要
実行速度 ◯(検索+推論) ◎(即応)
精度の安定性 ◯ 安定 △ 調整による
用途 FAQ・文書検索 特定業務への最適化

🎯 どちらを使うべきか?

目的 選択すべき手法
社内文書を検索しながら答えさせたい ✅ RAG
マニュアルからFAQを生成させたい ✅ RAG
専門用語に強いAI秘書を作りたい ✅ チューニング(LoRA)
自分の書き癖や口調を覚えさせたい ✅ チューニング(LoRA)

まずは、「Ollama + RAG(ローカルでの知識追加)」の構築方法を教えて下さい。なおOllamaはWindows11で動いています

Windows 11 上で動いている Ollama に ローカル知識を与える(RAG構成) ためには、以下のステップを踏むのが最も簡単かつ安定です。

✅ 目標

「PDFやテキストをローカルに保存 → 質問 → Ollamaがその内容に基づいて答える」構成を作る。


🛠 必要なツール構成

ツール 目的
Ollama LLM本体(例:llama3)
LangChain or LlamaIndex チャットの実行+外部知識連携
ベクトルDB 知識の保存・検索(Chromaを使用)
Python スクリプト処理

🪜 手順一覧

① Python + ライブラリをインストール

  1. Python 3.10以上をインストール(公式サイト

  2. 以下をインストール:

pip install langchain llama-index chromadb ollama

③ 知識を追加する方法

  • docs/example.txt に内容を書いて保存(例:社内マニュアル、FAQなど)

  • PDFなら PyMuPDF を使って読み込めます:

pip install pymupdf
python
from langchain.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("docs/manual.pdf")

📁 まとめ:フォルダ構成例

rag_project/
├── docs/
│ └── example.txt ←知識ソース ├── rag_ollama.py ←実行スクリプト  

✅ 必要なインストール

以下をすべてインストール済みであることを確認してください:

bash
pip install -U langchain langchain-community langchain-ollama chromadb
ollama run nomic-embed-text # 埋め込み用モデル
ollama run llama3 # LLM本体

🆕 rag_ollama.py(最新版)

from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_ollama import OllamaEmbeddings, OllamaLLM

# 1. 知識となるテキストを読み込み
loader = TextLoader("docs/example.txt", encoding="utf-8")
documents = loader.load()

# 2. テキストをチャンクに分割
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
texts = text_splitter.split_documents(documents)

# 3. Ollama埋め込みモデルでベクトル化
embedding = OllamaEmbeddings(model="nomic-embed-text")

# 4. ChromaでベクトルDBに保存(自動永続化)
db = Chroma.from_documents(texts, embedding, persist_directory="./chroma_db")

# 5. Ollama本体モデル(例:llama3)
llm = OllamaLLM(model="llama3")

# 6. RAG構成(Retriever + LLM)
qa = RetrievalQA.from_chain_type(llm=llm, retriever=db.as_retriever())

# 7. 対話ループ
print("終了したいときは 'exit' または 'quit' と入力してください。\n")
while True:
    query = input("質問 > ")
    if query.strip().lower() in ["exit", "quit"]:
        break
    result = qa.invoke({"query": query})
    print("答え:", result["result"])

📁 フォルダ構成例

rag_project/
                  ├── rag_ollama.py
                  ├── chroma_db/ ← 自動生成される(永続保存)
                  └── docs/
                                 └── example.txt ← 知識として使いたい文書

📌 注意点

  • .persist()不要(Chromaが自動保存)

  • import元はすべて langchain_community または langchain_ollama に変更済み

  • run() ではなく .invoke() を使用

いま実施して気が付いたのですが、RAGはText(Sample.txt)に記載されたこと以外のことはこたえられないのですか? 

ollama run llama3

ではこたえられることが、

python rag_ollama.py

ではこたえられません。

はい、その気づきはまさにRAGの本質を突いています。
ご理解のとおり:


✅ 【結論】

RAGでの回答は「与えられた知識(たとえば sample.txt)に基づくもの」に限定されます。


🔍 なぜか?

🔁 モードの違い

実行コマンド 動作内容 回答の知識源
ollama run llama3 LLMが直接回答 LLaMA3があらかじめ学習した膨大な知識(Wikipediaなど)
python rag_ollama.py RAG構成で回答 あらかじめ与えられたテキストファイルのみ

つまり、RAGは「知らないことは答えない」ように設計されています。

で、いま、このハイブリッド方法についてコーディングしているんですが、上手く動いていません。