【我が愛しのRaspberryPi】雑記2024年12月第2週記

# はじめに

自宅に置いてある大量のraspberry pi。
2025年から時間をやりくりして触って行こう。
本来であれば、8月くらいに試す時間があったんだけど、びっくりするほど熱くなってたから冬まで使用を控えていたのです。

思い返せば、Mathematicaを無料で使えるOSということで、存在を知り、Lチカを試したく、Raspberry Pi 1 Model B+を手にしてから、早10年。

正直、今は完全に忘れて多分Lチカも出来ない。
でも、raspberry pi B+, Raspberry Pi 2 Model B, Raspberry Pi 3 Model B, Raspberry Pi 5は全部で20台くらい眠っているので、そろそろ本格活用したいな。

現在は、Raspberry Pi 3 Model Bにipfireを入れて、頑張って活躍してくれている以外、本当に眠っているだけなのだ。
（年内にipfireをインストールし直すので、UART接続の方法から整理も兼ねて記事にしたいと思っています。）

購入したときはIoT周りをやりたかったんだけど、いかんせん業務と直結しないからモチベーションが沸かず、ただのコレクターと化してしまっているのが現状。


## 蛇足
Raspberry Pi 4 Model Bを購入したかったんだけど、半導体不足で手に入らないわ、転売では4～5万していたので、購入できないままRaspberry Pi 5が発売されたので、結局買えなかった。

# ACイラストに投稿したものの、却下されたラズパイ5君

頑張って書いたけど、審査された結果、却下されてしまったので、解説記事に使うことにします。
ラズベリーの場所がRaspberry Pi 5のものです。

![raspberrypi5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530912/b7ac29cf-2c62-dffe-fc24-54de2ce39a39.png)

なんだかんだで一番使っているのがRaspberry Pi 3 Model Bなので、Raspberry Pi 3 Model Bのイラストも作っていたんだけど、作っている途中に却下メールが来てずっと止まっているんですよね。

# 2024年12月8日

Halioも購入したし、是非使ってみたいんだけど、せっかく使おうと決めたのに環境構築で躓いては元も子もないのでまずは慣れていることから始めますか。
ということで、AWS Bedrockを使うクライアントOSとして活用することにしました。

[LangChainとLangGraphによるRAG・AIエージェント［実践］入門 エンジニア選書](https://www.amazon.co.jp/dp/B0DK4YGYBL?tag=xpg-22&linkCode=ogi&th=1&psc=1)
の検証機として使わせていただきましょう。

## 初期設定
Raspberry Pi ImagerでRaspberry Pi OS(64bit)をインストールし、

```
sudo apt update -y
sudo apt full-upgrade -y
sudo apt autoremove -y
```
を実行

# AWS CLIをインストール(失敗1)

```
sudo apt install awscli -y
sudo pip3 install awscli --upgrade
```
上記コマンドでAWS CLIをインストールしようとしたところ

システムのPython環境を保護するエラーが発生するので、venvで実行する必要あり。
ChatGPT先生がいるおかげで、エラーは簡単に解消出来るからありがたいですな。

```
sudo pip3 install awscli --upgrade
error: externally-managed-environment

× This environment is externally managed
mq> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.

    For more information visit http://rptl.io/venv

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
```

# AWS CLIをインストール(失敗2)

```
python3 -m venv ~/awscli-venv
source ~/awscli-venv/bin/activate
pip install awscli
```

を実行してリトライ

```
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting awscli
  Downloading https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl (4.5 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq?qqqq 4.0/4.5 MB 84.7 kB/s eta 0:00:07
ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
    awscli from https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl#sha256=27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060:
        Expected sha256 27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060
             Got        415e9255c66ae3aa466d580f9b7eeffab9ff440617b0abc0f1ba96f8e5a45d4a
```

またもやエラー発生。

ダウンロードされたAWS CLIパッケージのハッシュ値が期待されるものと一致しないことを示しています。
これは、パッケージが改ざんされた可能性があるか、リポジトリの同期に問題がある可能性があります。
とのこと。
ChatGPT先生がいるおかげで、エラーは簡単に解消出来るからありがたいですな。（2回目）


# AWS CLIをインストール(失敗3)

- キャッシュをクリアして再試行

```
pip cache purge
pip install awscli
```

```
Files removed: 2
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting awscli
  Downloading https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl (4.5 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqq?qqqqqqqqqq 3.4/4.5 MB 60.6 kB/s eta 0:00:20
ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
    awscli from https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl#sha256=27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060:
        Expected sha256 27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060
             Got        a34c56460bda53c55687fbec0cf3e0ece966cb843c9941435e157833d7f0417a
```

# AWS CLIをインストール(失敗4)

- 仮想環境を再作成

```
deactivate
rm -rf ~/awscli-venv
python3 -m venv ~/awscli-venv
source ~/awscli-venv/bin/activate
pip install awscli
```

```
pip install awscli
Files removed: 2
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting awscli
  Downloading https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl (4.5 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqq?qqqqqqqqqq 3.4/4.5 MB 60.6 kB/s eta 0:00:20
ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
    awscli from https://www.piwheels.org/simple/awscli/awscli-1.36.17-py3-none-any.whl#sha256=27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060:
        Expected sha256 27a77026939608f06369ea1944943251e99968e0b568b4f0f0e8c34876570060
             Got        a34c56460bda53c55687fbec0cf3e0ece966cb843c9941435e157833d7f0417a
```


# AWS CLIをインストール(成功)

はい。GPT先生ではなく、大人しく先人の知識を拝借することにいたします。

## インストールコマンド
```
rm -rf ~/awscli-venv
curl -O "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip"
python3 -m pip install https://github.com/boto/botocore/archive/v2.tar.gz
sudo ./aws/install
aws --version
```
```
aws-cli/2.22.12 Python/3.12.6 Linux/6.6.62+rpt-rpi-2712 exe/aarch64.debian.12
```

## AWS Configure

```
aws configure
```

## コマンド正常性確認
```
aws s3 ls
```

うしっ！成功！
ここらへん、CentOSとかだったらサラッと進めたんだけど、たまーに躓くんだよなぁ。
気持ち悪いので、後日OSから作り直して、正常手順のみ確認してみよ。

# AWS Bedrockの動作確認

## 実行のためのパッケージインストール(後日手順整理)

pip installでエラーが出たので、環境構築は下記コマンドで無理やり完了させる。
非推奨のインストール方式なので、OS作り直してvenvで手順を確立させます。

```
pip install langchain-aws  --break-system-packages
wget https://www.piwheels.org/simple/botocore/botocore-1.35.76-py3-none-any.whl
pip install botocore-1.35.76-py3-none-any.whl
pip install botocore-1.35.76-py3-none-any.whl  --break-system-packages
pip install langchain-aws  --no-chache-dir
pip install langchain_community --break-system-packages
```

## 共通ライブラリ作成

参考書はOpenAIを使用しているので、Bedrock用にライブラリを作成しておく

```lib.LLMClient.py
from langchain_aws import ChatBedrock, BedrockEmbeddings
from langchain_community.embeddings import BedrockEmbeddings

class LLMClient:
    def __init__(self, model_id, region_name, max_tokens=1000, temperature=0.7, embedding_model_id="amazon.titan-embed-text-v1"):
        self.llm = ChatBedrock(
            model_id=model_id,
            region_name=region_name,
            model_kwargs={
                "max_tokens": max_tokens,
                "temperature": temperature
            }
        )
    
    def stream(self, messages):
        return self.llm.stream(messages)

    def with_structured_output(self, messages):
        return self.llm.with_structured_output(messages)

    def __call__(self, messages):
        return self.invoke(messages)
```

## サンプルプログラム

```sample.py
from lib.LLMClient import LLMClient
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage

# LLMClientのインスタンス化
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
region_name = "ap-northeast-1"
model = LLMClient(model_id=model_id, 
                           region_name=region_name, 
                           max_tokens=2500, 
                           temperature=0)
messages = [
    SystemMessage(content="You are a helpful assistant."),
    HumanMessage(content="こんにちは！"),
]

for chunk in model.stream(messages):
    print(chunk.content, end="", flush=True)
```

```
こんにちは！お元気ですか？何かお手伝いできることはありますか？ご質問やお話したいトピックがあれば、お気軽にお聞かせください。
```


## つぶやき

langchainはとっとと学習を完了させないと、次バージョンが出てしまうんですよね。
新バージョンについていく前に、基本的な機能を試しておかないと・・・
さりげに学習と言えど、スピード勝負ってところがありますな。
環境構築にこれだけ苦戦してしまうと、Chromaとか使うまで、そこそこ苦戦強いられそうですなぁ。
raspberry piではなく使い慣れているCentOSの方が速攻出来るけど、我が愛しのRaspberryPiに活躍の場を提供させたい！（寒い季節の間に！）

# 2024年12月9日

昨日、確認した箇所まではスンナリ動作確認出来たんだけど、
[LangChainとLangGraphによるRAG・AIエージェント［実践］入門 エンジニア選書](https://www.amazon.co.jp/dp/B0DK4YGYBL?tag=xpg-22&linkCode=ogi&th=1&psc=1)
の「4.6章 vector store」で
```sample.py
from langchain_chroma import Chroma

db = Chroma.from_documents(docs, embeddings)
```
を実行するとクラッシュしてしまった。
https://qiita.com/LiberalArts/items/4a1b8ca844a6c27be1b4
に記載していただいている処理は実行できたので、単純にスペック不足かな？
だとすると、raspberry piでこの処理を行うのは諦めよう。

## 初期設定

環境構築ってタイムスタンプが重要なので、最終確認日をメモ
- 2024年12月9日22時時点成功

```
sudo apt update -y
sudo apt full-upgrade -y
sudo apt autoremove -y
```

## AWS CLIのインストール

環境構築ってタイムスタンプが重要なので、最終確認日をメモ
- 2024年12月9日22時時点成功

```
python3 -m venv ~/my_work
source ~/my_work/bin/activate
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc
source ~/.bashrc
aws --version
```

## LangChain学習までの準備

環境構築ってタイムスタンプが重要なので、最終確認日をメモ
- 2024年12月9日22時時点成功


```
source ~/my_work/bin/activate
wget https://www.piwheels.org/simple/botocore/botocore-1.35.76-py3-none-any.whl
pip install botocore-1.35.76-py3-none-any.whl
pip install langchain-aws 
pip install langchain_community
pip install sentence-transformers
pip install -U langchain-huggingface
pip install langchain-chroma==0.1.4
pip install langchain-community==0.3.0 GitPython==3.1.43
pip install langchain-text-splitters==0.3.0
```

## 動作確認プログラム

embeddingの処理を有効にしていなかったので、HuggingFaceEmbeddingsで代用しました。

```my_work.py
from langchain_community.document_loaders import GitLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma

def file_filter(file_path: str) -> bool:
    """Markdownファイルを選択するフィルター関数。"""
    return file_path.endswith(".mdx")

def load_documents(clone_url: str, repo_path: str, branch: str) -> list:
    """Gitリポジトリからドキュメントを読み込む。"""
    loader = GitLoader(
        clone_url=clone_url,
        repo_path=repo_path,
        branch=branch,
        file_filter=file_filter,
    )
    raw_docs = loader.load()
    print(f"{len(raw_docs)} 件のドキュメントを読み込みました。")
    return raw_docs

def split_documents(raw_docs: list, chunk_size: int = 1000) -> list:
    """ドキュメントをチャンクに分割する。"""
    text_splitter = CharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=0)
    docs = text_splitter.split_documents(raw_docs)
    print(f"{len(docs)} 件のチャンクに分割しました。")
    return docs

def create_embeddings(model_name: str, cache_folder: str) -> HuggingFaceEmbeddings:
    """指定されたモデルを使用して埋め込みを作成する。"""
    return HuggingFaceEmbeddings(model_name=model_name, cache_folder=cache_folder)

def embed_query(embeddings: HuggingFaceEmbeddings, query: str) -> list:
    """クエリ文字列を埋め込む。"""
    vector = embeddings.embed_query(query)
    print(f"クエリベクトルの長さ: {len(vector)}")
    return vector

def create_database(docs: list, embeddings: HuggingFaceEmbeddings) -> Chroma:
    """ドキュメントと埋め込みからChromaデータベースを作成する。"""
    input("ここでクラッシュするので、Ctrl+Cを押して終了")
    db = Chroma.from_documents(docs, embeddings)
    print("データベースが作成されました。")
    return db

# メイン実行フロー
if __name__ == "__main__":
    clone_url = "https://github.com/langchain-ai/langchain"
    repo_path = "./langchain"
    branch = "master"
    
    raw_docs = load_documents(clone_url, repo_path, branch)
    
    docs = split_documents(raw_docs)
    
    model_name = "intfloat/multilingual-e5-large"
    cache_folder = "./sentence_transformers"
    
    embeddings = create_embeddings(model_name, cache_folder)
    
    query = "AWSのS3からデータを読み込むためのDocument loaderはありますか？"
    
    vector = embed_query(embeddings, query)
    
    db = create_database(docs, embeddings)
```

## ChromaDB(CRUD)

ベクトルDBでCRUDを最初にやるというのは正しいか分からないけど、手探り次第で実行し成功したコード。
まぁ、分からないなりに手探りで進めるしかないでしょうね。

```chroma_db_base_program.py
import chromadb

# Chromaクライアントの初期化
client = chromadb.Client()

# データベースの作成
collection_name = "raspberry241209"
collection = client.create_collection(collection_name)

# Create: 新しいデータを追加する関数
# Create: 新しいデータを追加する関数
def create_data(data_list):
    ids = [f"id{i}" for i in range(len(data_list))]  # 一意のIDを生成
    collection.add(documents=data_list, metadatas=[{"source": "user"}]*len(data_list), ids=ids)
    print(f"{len(data_list)} 件のデータを追加しました: {data_list}")

# Read: データを取得する関数
def read_data():
    # 特定の文書を検索するためのテキスト
    query_text = "最初のデータ"
    results = collection.query(query_texts=[query_text])  # query_texts引数を使用
    print("取得したデータ:")
    for doc in results['documents']:
        print(doc)

# Update: データを更新する関数
def update_data(old_id, new_data):
    # 特定のIDを持つドキュメントを更新
    collection.update(
        ids=[old_id],            # 更新するIDを指定
        documents=[new_data],    # 新しいデータで更新
        metadatas=[{"source": "user"}]  # メタデータも必要に応じて指定
    )
    print(f"データを更新しました: ID={old_id} -> {new_data}")


# Delete: データを削除する関数
def delete_data(id):
    # 特定のIDを持つドキュメントを削除
    collection.delete(ids=[id])  # IDsで指定して削除
    print(f"データを削除しました: ID={id}")

# 使用例
if __name__ == "__main__":
    # 複数のデータを追加
    create_data(["最初のデータ", "二番目のデータ", "三番目のデータ", "四番目のデータ", "五番目のデータ", 
                  "六番目のデータ", "七番目のデータ", "八番目のデータ", "九番目のデータ", "十番目のデータ"])
    read_data();
    update_data("id0", "更新された最初のデータ")
    read_data();
    delete_data("id1");
    read_data();
```

## つぶやき

なんだかんだ言っても、遠回りが一番早いので、ChromaDBの学習からやってみますか。
Chromaに対する知識がほぼゼロなので、Chromaの動作確認端末として直近raspberry pi 5さんには活躍してもらうことにいたしましょう。

時間がないといっても、一度触りだすと止まらないのがraspberry piなんだよなぁ。

### 覚書 at 24'12/09
眠っていたraspberry piを呼び起こしてやりたいことが出てきたので、箇条書きにして覚えておこう。
- サブマシンとして活用（遊んでるパソコンと分離した環境で開発したいので、どこまで出来るか確認）
- Lチカ（せっかく過去に得た知識なので、腐らせず思い出していけばIoTへの道筋になるかも）
- Halio（コレクションと化しているので動作確認はしておかねば）
- DS3231（夏場は最小限で動作させる必要があるから自動起動が出来るか確認しておく）
- raspberry pi 3はDMZとして活用したい
- Voiceパッケージのインストール（SIPサーバ構築で遊んでいた時の知識を復活させたい）
直近は、まぁサブマシンとしてどこまで活用出来るかの見極めですかね。できればベクトルDBとして使いたいので、ChromaもしくはQdrantのお勉強になりそうですね。

# 2024年12月10日

今日は、ChromaDBについて簡単に確認しておこう。
raspberry pi 5 8GBが本当にLangChainのChromaDBとして使えないのか、使う方法があるのか、今週ちょっと見極めておきたいし。

## 初期設定

環境構築ってタイムスタンプが重要なので、最終確認日をメモ
- 2024年12月10日22時時点成功

```
sudo apt update -y
sudo apt full-upgrade -y
sudo apt autoremove -y
```

## AWS CLIのインストール

環境構築ってタイムスタンプが重要なので、最終確認日をメモ
- 2024年12月10日22時時点成功

```
python3 -m venv ~/my_work
source ~/my_work/bin/activate
pip install --upgrade chromadb
pip install langchain
pip install -U langchain-community
pip install -U langchain-huggingface
pip install -U langchain-chroma
pip install sentence_transformers
```

## ChromaDBの動作確認(retrieverまで)

```sample_code1.py
from chromadb.utils import embedding_functions
from chromadb.api.models.Collection import Collection
import chromadb

def create_chroma_db(documents, embedding_model="all-MiniLM-L6-v2", collection_name="my_collection"):
    # クライアントの作成
    client = chromadb.Client()

    # 埋め込み関数の設定
    embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(model_name=embedding_model)

    # コレクションの作成
    collection = client.create_collection(
        name=collection_name,
        embedding_function=embedding_func
    )

    # ドキュメントの追加
    collection.add(
        documents=documents,
        ids=[f"id{i}" for i in range(len(documents))]
    )

    print(f"{len(documents)}件のドキュメントを含むコレクションを作成しました。")
    return collection

# 使用例
docs = ["これは1つ目のドキュメントです。", "これは2つ目のドキュメントです。", "これは3つ目のドキュメントです。"]
my_db = create_chroma_db(docs)
```

```sample_code2.py
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
from langchain.schema import Document

# HuggingFace の埋め込みモデルを使用
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

vectorstore = Chroma(
    collection_name="my_collection",    # ベクトルストア内のコレクション名を指定します。これにより、複数のコレクションを同じデータベース内で管理できる
    embedding_function=embeddings,      # 文書をベクトル化するための埋め込み関数を指定
    persist_directory="./chroma_db"     # ベクトルストアのデータを永続化するディレクトリを指定
)

# Documentオブジェクトを作成（メタデータ付き）
documents = [
    Document(page_content="山路を登りながら、こう考えた。", metadata={"author": "夏目漱石", "work": "草枕", "chapter": 1}),
    Document(page_content="智に働けば角が立つ。情に棹させば流される。", metadata={"author": "夏目漱石", "work": "草枕", "chapter": 1}),
    Document(page_content="意地を通せば窮屈だ。", metadata={"author": "夏目漱石", "work": "草枕", "chapter": 1}),
    Document(page_content="とかくに人の世は住みにくい。", metadata={"author": "夏目漱石", "work": "草枕", "chapter": 1}),
    Document(page_content="住みにくさが高じると、安い所へ引き越したくなる。", metadata={"author": "夏目漱石", "work": "草枕", "chapter": 1})
]
ids = ["doc1", "doc2", "doc3", "doc4", "doc5"]

vectorstore.add_documents(documents, ids=ids)

# リトリーバーの作成
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
query = "吾輩は猫である"

context_docs = retriever.invoke(query)
print(f"len = {len(context_docs)}")

first_doc = context_docs[0]
print(f"metadata = {first_doc.metadata}")
print(first_doc.page_content)
```

## つぶやき

とりあえず、retriverまで確認。
sample1.pyから、LLM本の方に派生するのが難しかったので、sample2.pyのコードに方針転換。
5件程度なら普通に動くのね。
ちなみに、私は夏目漱石で一番好きなのは「草枕」なんだけど、何故か好きなのかは分からないんだよな。内容としては「こころ」の方が記憶に残ってるし。
冒頭の文章が、高校時代に突き刺さったからなのか・・・
あとは、10,20,40,80,160,320,640,1280,2560と耐久テストをしてみますか。
参考書のコードからは変わるけど、多少はうまくいきそうな兆しが見えてきた。
色々設定いじったので、クリーン環境で再現確認してから動作検証かな。
忙しくなってきたので、次に使えるのは週末になりそうだ。
上手く行けば、ChromaDB用として、raspberry piを活用できるかも！