MacBook Proのターミナルが開かなくなった

突然ですが、Pythonのスクリプト起動中にターミナルが使えなくなりました、正確にはターミナルが消滅して、起動しようとしても以下のメッセージ出て起動ができない

ターミナル使えなかったらパソコンとしては無価値だから代替え手段を探すとiTermというのがあるらしいのでインスト

https://iterm2.com

無論コマンドラインではできないからappをダウンロードして起動すると、

普通に使えそうです良かった、というかターミナルの強化版だから、最初からiTerm使ってた方が良かったんじゃないかな

他にないか調べると、Rustで書かれたAlacirttyというのもあるからこれも入れてみた

https://alacritty.org/index.html

dmgだからターミナルなくてもインストできる

P.S. 2025/4/1

原因を探ってみると、ChatGPTの回答の参照リンク(以下)

https://qiita.com/koma3/items/5df98663463571a14272

に該当していて、以下の対応ディレクトリ(ちょうど起動出来なくなった頃のタイムスタンプのファイルが存在)を一時的にデスクトップに対比すると起動出来ました、まあ標準ターミナルはもう使わないけどね

 

admin

YOLOを使ってみる

ラズパイ5でカメラから取り込んだ画像の処理は、今ならYOLOがそこそこの精度で使えそうだからやってみた

全体の流れと動作確認できるPythonスクリプト

https://techblog-ai.com/【raspberry-pi-5xカメラ】yolov8-リアルタイム推論-usbカメラ-picameraモ/

Yoloのインスト

https://techblog-ai.com/【raspberry-pi-5】yolov8のインストール方法/

但しpipでpicameraインストしてはあかんらしい(見つからないと言われたよ)から、aptでインストした

$ sudo apt install -y python3-picamera2

前の仮想環境(venv)は汚染されたぽいから、新たな仮想環境(venv2)を作る、picameraも別環境だから再度インスト必要

静止画像からの検出スクリプトを動かしてみると

$ cat yolo.py
from ultralytics import YOLO
model = YOLO("yolov8x.pt")
results = model('test.png', save=True)

学習済みモデルの最高精度版(yolov8x.pt)をダウンロードしてきて検出してます、これは壁紙のスクショ(test.png)をターゲットに実行、結果の画像は省略

次にラズパイカメラの動画からの検出は、

こんな感じで、モバイル用の辞書(yolov8n.pt)でも精度は高そうですね

またフレーム周波数は数フレームぐらいでは動いてくれているようです

Yoloは負荷重いから、CPU温度は60℃ぐらいまでアップしてファンは常時回転してますが

 

admin

ラズパイ5のPythonでの音声入力デバイス指定

USBマイクの接続先をUSB2.0のコネクタからUSB3.0にしたら同じコードで入力がされない状態に、コメントアウトしている# device=2,を指定しないとダメ

import vosk
import pyaudio
import json
import numpy as np
import sounddevice as sd
import queue
import threading
import time

class VoskSpeechRecognizer:
    def __init__(self, model_path='./vosk-model-ja-0.22'):
        # モデルの初期化
        vosk.SetLogLevel(-1)
        self.model = vosk.Model(model_path)
        self.recognizer = vosk.KaldiRecognizer(self.model, 16000)
        
        # キュー設定
        self.audio_queue = queue.Queue()
        self.stop_event = threading.Event()
        
        # マイク設定
        self.sample_rate = 16000
        self.channels = 1
        
        # スレッド準備
        self.recording_thread = threading.Thread(target=self._record_audio)
        self.recognition_thread = threading.Thread(target=self._recognize_audio)
        
    def _record_audio(self):
        """
        連続的な音声録音スレッド
        """
        with sd.InputStream(
            samplerate=self.sample_rate, 
            channels=self.channels,
            dtype='int16',
            # device=2,
            callback=self._audio_callback
        ):
            while not self.stop_event.is_set():
                sd.sleep(100)
    
    def _audio_callback(self, indata, frames, time, status):
        """
        音声入力のコールバック関数
        """
        if status:
            print(status)
        self.audio_queue.put(indata.copy())
    
    def _recognize_audio(self):
        """
        連続的な音声認識スレッド
        """
        while not self.stop_event.is_set():
            try:
                audio_chunk = self.audio_queue.get(timeout=0.5)
                if self.recognizer.AcceptWaveform(audio_chunk.tobytes()):
                    result = json.loads(self.recognizer.Result())
                    text = result.get('text', '').strip()
                    if text:
                        print(f"{text}")
            except queue.Empty:
                continue
    
    def start_recognition(self):
        """
        音声認識の開始
        """
        self.stop_event.clear()
        self.recording_thread.start()
        self.recognition_thread.start()
    
    def stop_recognition(self):
        """
        音声認識の停止
        """
        self.stop_event.set()
        self.recording_thread.join()
        self.recognition_thread.join()

def main():
    recognizer = VoskSpeechRecognizer()
    
    try:
        print("音声認識を開始します。Ctrl+Cで終了できます。")
        recognizer.start_recognition()
        
        # 無限ループを防ぐ
        while True:
            time.sleep(1)
    
    except KeyboardInterrupt:
        print("\n音声認識を終了します...")
    finally:
        recognizer.stop_recognition()

if __name__ == "__main__":
    main()

原因はいまいち不明ですが、もとのソースにマイクのチャネル指定はなかった訳だけど、USB3.0側にすると明示しないとダメそうです

sounddeviceで検索すると、0指定にするのが正しそうですが、0にするとエラーが出ます

2はシステムのデフォルト指定なので、それはそれで正しそう、但し2が常にデフォルトではあると限らないから、

device= “sysdefault”,

とデフォルト指定と明示した方が、音声入力手段が複数あれば別ですが、一個しか無いんだったらこれが汎用で使えそうです

$ python
Python 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sounddevice as sd
>>> print(sd.query_devices())  # すべてのデバイス一覧を表示
   0 SF-558: USB Audio (hw:0,0), ALSA (1 in, 0 out)
   1 UACDemoV1.0: USB Audio (hw:2,0), ALSA (0 in, 2 out)
   2 sysdefault, ALSA (128 in, 0 out)
   3 spdif, ALSA (1 in, 0 out)
   4 lavrate, ALSA (128 in, 0 out)
   5 samplerate, ALSA (128 in, 0 out)
   6 speexrate, ALSA (128 in, 0 out)
   7 pulse, ALSA (32 in, 32 out)
   8 speex, ALSA (1 in, 0 out)
   9 upmix, ALSA (8 in, 0 out)
  10 vdownmix, ALSA (6 in, 0 out)
* 11 default, ALSA (32 in, 32 out)

 

admin

ラズパイ5のドライブをSSDにする

そこそこSDカードの中身も落ち着いてきて、サイズも20GB/64GBぐらいになってきたので、運用寿命と速度を勘案してSSDにしました

仕様は写真から読み取れますが、アダプタボードとNVMe仕様のSSD、サイズは256GBでおそらく一生持ちそう

ディスクの作成は、SD Card Copierを使って行いました、

New Partition UUIDsにチェックは重要、チェックしないと完全クローンになるので、SDカードとSSD同時実装時に区別つきません

最初、チェックしないでコピーしたのでリカバリに苦労、以下LLMに聞きながら実行した手順、要は「MBR/GPTヘッダ完全削除
」をしないとダメのようです

同じUUIDでコピーした時のリカバリ手段、macで単純ディスク消去だと
MBR/GPTヘッダが残ってるらしく不完全な初期化です

$ diskutil list # SSDのドライブ番号を検索する(/dev/disk4 だった)
$ diskutil unmountDisk /dev/disk4	# 外付けSSD番号を指定
# MBR/GPTヘッダ完全削除
(初期化するからディスク名や形式は重要ではないはず)
$ diskutil eraseDisk JHFS+ NewDiskName /dev/disk4
$ sudo dd if=/dev/zero of=/dev/disk4 bs=1M count=100

インストールして、bootデバイスを探す時間は無駄なのでSSDを一番最初にアクセスするような設定に変更します

$ sudo rpi-eeprom-config --edit
 ファイルの461 -> 416に変更(右から順番に探して、6はSSDです)

$dfの結果はこんな感じ、

電源オン起動時間は体感でも明らかに早いし、アプリ(例えばブラウザ)の起動も早い、VNCでダミーHDMI入れてないから、相変わらずフレームレートは出ないけども

P.S. デスクアクセス性能

以前SDカードの値があったので比較してみる

$ sudo curl https://raw.githubusercontent.com/TheRemote/PiBenchmarks/master/Storage.sh | sudo bash


     Category                  Test                      Result     
HDParm                    Disk Read                 90.82 MB/sec             
HDParm                    Cached Disk Read          90.94 MB/sec             
DD                        Disk Write                32.5 MB/s                
FIO                       4k random read            6317 IOPS (25268 KB/s)   
FIO                       4k random write           845 IOPS (3382 KB/s)     
IOZone                    4k read                   29989 KB/s               
IOZone                    4k write                  3288 KB/s                
IOZone                    4k random read            30032 KB/s               
IOZone                    4k random write           3268 KB/s                

                          Score: 2530                             


     Category                  Test                      Result     
HDParm                    Disk Read                 441.07 MB/sec            
HDParm                    Cached Disk Read          432.23 MB/sec            
DD                        Disk Write                345 MB/s                 
FIO                       4k random read            87521 IOPS (350085 KB/s) 
FIO                       4k random write           85333 IOPS (341333 KB/s) 
IOZone                    4k read                   132985 KB/s              
IOZone                    4k write                  169992 KB/s              
IOZone                    4k random read            63871 KB/s               
IOZone                    4k random write           181370 KB/s              

                          Score: 39288                                       

今時のベンチマークに比較して、めちゃくちゃ早いわけではないが、妥当な値と言えるだろう

 

admin

Difyにナレッジを追加

前回、ローカルにgemma3:4bをOllamaで動かしてDifyをインストだけして、Pythonからもアクセスしてみましたが、Difyの主たる機能であるRAGの手始めにナレッジを追加して割と最近の情報も活用するようにしてみた

参考は、

テキストファイルの追加は、

https://tech-lab.sios.jp/archives/46102

crawlingについては、

https://zenn.dev/zozotech/articles/d177f4cdc02755

追加したのはMacのメモを画像を除外してtxtファイル一本にまとめたものと、Web URLからcrawlingインストしてスクレーピングしたもの、

この状態でナレッジを有効化してポッドにプロンプト送ると、

ナレッジの参照先がリンクとして表示されてます

Macのファンが何故か回りっぱなしになってます、アイドル90%ぐらいなのに

 

admin

gemma3は優れものだね

今月公開されたようですが、Googleのgemma3は4bぐらいだと普通に回答のレベルも高いから、応答時間含めて実用レベルです

1b/4b・・・とありますが、実は1bだとラズパイ5でも普通に動いた、swapは起きてますが

モデルサイズも1G以下でコンパクト、

但しMacで動かした4bと比較すると精度は違う、以下は1bのレスポンス

4bだと、

と言う感じですから、差は明らか

次にDifyでモデルを、以下を参考にGeminiからollamaに変えてみた

https://note.com/dify_lab/n/n09e680c825cf

Difyからの接続設定

使えるモデルは、以下のように二つになります

どちらを使うかは、モデルのところをクリックしてこれはollmaに変えた後、

メモリ使用量もそれほどでもない、

と言うことで、gemma3は期待に違わずよく出来てます、

 

admin

Mac(32GBメモリ)のOllamaで動かせるモデル

パラメータ数が多くなるとメモリとGPUの能力への要求レベルが高くなりますが、32GBのM4 MacBook Proでどの程度まで動くのか試してみた

モデルは以下の通り、

% ollama ls
NAME               ID              SIZE      MODIFIED          
gemma2:27b         53261bc9c192    15 GB     About an hour ago    
mixtral:latest     a3b6bef0f836    26 GB     2 hours ago          
elyza:jp8b         e81c07bbe038    4.9 GB    4 weeks ago          
llama3.2:latest    a80c4f17acd5    2.0 GB    4 weeks ago

 

mixtralだと、アクティビティモニターが黄色(ほぼ赤に近い)レベルまでメモリを食い尽くすから使えない、使って初めてスワップも発生

ハードリソース的にほぼ限界かと思えるのが、gemma2:27bでこの時のメモリモニターを見ると、メモリは最大近くまで使う感じ

ちなみにOllamaの情報見ると、仮想メモリサイズが1.48TBとか、この数値はSSDのサイズ1Tも超えているんですが、

ということで、モデルのパラメータ数で30b程度が32GBメモリのMacで動かせる限界のようです

ちなみにこのサイズのパラメータで初めて日本で一番高い山の回答が正しく返ってくるから、それ以下のパラメータでは実用性はそれほどないのかもしれない

 

admin

Difyのupdate(V1.1.0)

版数がこんな感じに表示(すでにアップデート後ですが)されてクリックするとアップデート手順が案内されます

手順はこんな感じ、日付付きのバックアップを取るようになってます

リリースノートも同じページにあります

 

admin

 

ラズパイ5で日本語入力

vimで日本語入力できないなと思ったら、そもそも今までラズパイで日本語入力使ったことがなかったのだ、

ツールとしてはfcitx-mozcを使うと日本語は入力できるようにはなる、ただし使い勝手はイマイチ感あるから、Macのエディタで作成したものを貼り付けるのが実用的だな、と思ったから非常手段かもしれない

 

admin

 

 

Difyをローカルで動かす(さわり)

ローカルで動かせて、かつカスタマイズが簡単(ノーコードで実現可能)なLLM(モデルプロバイダを設定)が使えるプラットホームです、Webページのこのメッセージが全てかもしれません、一番特徴的なのはRAGエンジンじゃないかと思いますね

============

DifyはオープンソースのLLMアプリ開発プラットフォームです。RAGエンジンを使用して、エージェントから複雑なAIワークフローまでLLMアプリを編成します。

============

<インストール>

・ターゲット:M4 MacBook Pro 14

・Docker :Engine: 28.0.1/Compose: v2.33.1-desktop.1

わざわざカスタムインストの意味はないから、Docker使って素直にインストします、以下にはRancher Desktopでインスト時のメモも残ってますが、ともかく4行のコマンド実行でDockerで動くようになります

% git clone https://github.com/langgenius/dify.git
% cd dify/docker
% cp .env.example .env	# 環境変数のコピー

# Rancher Desktopの場合には、
# Docker-compose.yamlの修正、ブラウザポートの重複回避のためにポート番号を変更(8888に)、661行目でした(@2015/3/16)

      - '${EXPOSE_NGINX_PORT:-8888}:${NGINX_PORT:-80}'

# 注)Rancher Desktopで再インストするとアクセスできない(8888開かない?)

% docker compose up -d
# デタッチモードでバックグランドで起動させる

Dify自体にLLM機能はなく外部サービスを利用する形態なので、モデルプロバイダーはGeminiを設定しています

DockerとRancher  Desktop両方を動かす時の切り替え方法と再インストール他のコマンド

% docker compose up --build
# imageの再ビルドを行う

% docker compose down
# containerを停止すると共に削除する

% docker compose down --rmi all
# これはimageを削除する

% docker compose stop(コンテナ停止)、start(コンテナ起動)


# Dockerの切り替え(Baker link. Env用にrancher desktopを優先させる)

% docker context use rancher-desktop

% docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT                                       ERROR
default             Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                           
desktop-linux       Docker Desktop                            unix:///Users/usamiryuuichi/.docker/run/docker.sock   
rancher-desktop *   Rancher Desktop moby context              unix:///Users/usamiryuuichi/.rd/docker.sock           


% docker context use desktop-linux 	# change to Docker

インストールから基本的な操作(応答を変化させるオーケストレーションとか)は以下のリンクで、

https://weel.co.jp/media/dify-local/

 

<Pythonから使う>

RAGはとりあえず置いといて、Pythonからの使い方

クラウドのDify使う時には、

https://zenn.dev/fa18kouki/articles/579ef29527a5d9

が参考になりますが、これをローカルに置き換えてます

APIキーは、以下の「APIアクセス」から取得します、また具体的なAPIの使い方ドキュメントも含まれます

import requests
import json
from typing import Dict

# Dify APIの認証キー
API_KEY = 'your API key'  # 取得したAPIキーに置き換えてください
# Dify APIのベースURL(ポート番号を含む)
BASE_URL = 'http://localhost/v1/chat-messages'

def get_dify_response(query: str, user: str) -> str:
    headers = {
        'Authorization': f'Bearer {API_KEY}',
        'Content-Type': 'application/json'
    }

    data: Dict[str, any] = {
        "inputs": {},
        "query": query,
        "response_mode": "streaming",
        "conversation_id": "",
        "user": user,
        "files": [
            {
                "type": "image",
                "transfer_method": "remote_url",
                "url": "https://cloud.dify.ai/logo/logo-site.png"
            }
        ]
    }

    try:
        response = requests.post(BASE_URL, headers=headers, json=data, stream=True)
        response.raise_for_status()

        # ここでエンコーディングを明示的に指定
        response.encoding = 'utf-8'

        full_response = ""
        for line in response.iter_lines():
            if line:
                decoded_line = line.decode('utf-8')
                if decoded_line.startswith("data: "):
                    json_data = json.loads(decoded_line[6:])
                    if json_data.get('event') == 'message':
                        raw_answer = json_data.get('answer', '')

                        # Unicodeエスケープのデコードを削除(不要)
                        print(raw_answer, end='', flush=True)
                        full_response += raw_answer

        print()
        return full_response
    except requests.RequestException as e:
        print(f"リクエストエラー: {e}")
        return str(e)
    except json.JSONDecodeError as e:
        print(f"JSON解析エラー: {e}")
        return "JSONの解析に失敗しました"
    except Exception as e:
        print(f"予期せぬエラー: {e}")
        return str(e)

def main():
    query = "Difyでできることは?"
    user = "user0"

    print("Difyへのクエリ:", query)
    answer = get_dify_response(query, user)
    print("\nDifyからの完全な応答:")
    print(answer)

if __name__ == "__main__":
    main()

エンドポイント(サービスの種類)はいくつかありますが、ここではBASE_URL = 'http://localhost/v1/chat-messages'を使っています

レスポンスは、以下のような内容で返ってきます

Difyへのクエリ: Difyでできることは?
Difyって、すごい魔法のツールなんだ!  

Difyは、色々なことができるよ。たとえば、

* **お話を書く**  
  Difyに「お姫様とドラゴンのお話を作って」ってお願いすると、面白いお話を作ってくれるんだ!  
* **絵を描く**  
  Difyに「虹色の猫の絵を描いて」ってお願いすると、カラフルな猫の絵を描いてくれるよ!
* **音楽を作る**  
  Difyに「楽しい音楽を作って」ってお願いすると、リズムの良い音楽を作ってくれるんだ!  
* **ゲームを作る**  
  Difyに「宝探しゲームを作って」ってお願いすると、楽しい宝探しゲームを作ってくれるよ!

Difyは、まだ成長中だけど、たくさんのことができるようになるんだって!  
Difyをもっと知りたい?  もっと詳しいことを教えてあげるよ!  
他にどんなことができるか、聞いてみてね! 


Difyからの完全な応答:
Difyって、すごい魔法のツールなんだ!  

Difyは、色々なことができるよ。たとえば、

* **お話を書く**  
  Difyに「お姫様とドラゴンのお話を作って」ってお願いすると、面白いお話を作ってくれるんだ!  
* **絵を描く**  
  Difyに「虹色の猫の絵を描いて」ってお願いすると、カラフルな猫の絵を描いてくれるよ!
* **音楽を作る**  
  Difyに「楽しい音楽を作って」ってお願いすると、リズムの良い音楽を作ってくれるんだ!  
* **ゲームを作る**  
  Difyに「宝探しゲームを作って」ってお願いすると、楽しい宝探しゲームを作ってくれるよ!

Difyは、まだ成長中だけど、たくさんのことができるようになるんだって!  
Difyをもっと知りたい?  もっと詳しいことを教えてあげるよ!  
他にどんなことができるか、聞いてみてね! 

モデルプロバイダーからのレスポンスをオーケストレーション設定で「子供に話すような回答」と設定しているので、Dify内で加工されたレスポンスになっています

 

admin