M1 vs M4の性能比較(非常に限定的な場合で)

M1 MacBook AirとM4 MacBook Pro 14inchの性能差ということですが、以下のリンクは素数計算をPerplexityでRustコード発生させて実行した時のもの

https://isehara-3lv.sakura.ne.jp/blog/2024/10/23/生成a-igeminiでコード生成rust/

<Geminiで生成したシングルスレッド版 : release mode>
2から1000000までの素数:
Elapsed time: 37.51125ms

<Geminiで生成したマルチスレッド版 : release mode>
2から1000000までの素数:
Elapsed time: 7.397958ms

以上はM1 MacBook Airでの結果


M4 MacBook Proで実行してみると(実行ごとにばらつくから5回実行してベストタイム)

<シングルスレッド版>
2から1000000までの素数:
Elapsed time: 27.473ms(1.36倍)

<マルチスレッド版>
2から1000000までの素数:
Elapsed time: 6.449875ms(1.15倍)

シングルスレッド(マルチコア対応)はそんなものかと思うけど、マルチコア対応のコードの実行速度はイマイチ、考えられる可能性は計算量に比較してオーバーヘッドが大きいせいじゃ無いかと思う

FusionやiMovieなどの立ち上げや書き出しは、はっきりわかるほどの差があるからね、非常に限定的なアプリではありますが、体感ではシングルスレッドの時の速度差ということになります、自分の使い方ではCPU速度よりもメモリとSSDの絶対量確保の方が重要

 

admin

M4 MacBook Pro 14ファーストインプ

M1 MacBook Airのリソース不足(メモリ16GB/SSD 512GB)で買い換えたM4 MacBook Pro 14(メモリ32GBにアップ、SSDは1TB、キーボードはUSモデル)が昨日到着したのでその感想、購入している間にM4 MacBook Airも発表されましたが、まあ欲しい時に買うのがベスト

個人的にAirに比較したProのアドバンテージは、

① Youtubeで音楽流してもスピーカーの音はかなりまとも、Airは音がしょぼい

② 拡張性:Appleの微妙なヒエラルキーでもありますが、HDMIとSDカードスロットはポートレプリケータなくても外出先で使えるのは便利

③ LCD表示品質:Airのような表示のケバケバしさはなくて自然な色合いと感じます、狭ベゼルでもあるのでスペースも有効活用

M4 Pro以上のチップは値段と性能のバランスから考えたら無い選択、使い方からしてもM4で良いかな

<移行>

同じApple siliconなのでM1 MacBook Airからそのままデータ移行、メールアカウントやアプリの認証だけは再設定必要

バックアップ(timemachine)はAirを引き継いでそのまま何の設定もなしにできた、ただしAirの最終状態からの差分ではなくてAirを初回にバックアップしてからの差分をバックアップしたようです

<使用感>

① ブラウザ(Safari, Chorme)、VScode、コンテナ(Podman, Rancher Desktop)立ち上げた状態で、LLM(ollamaの80億モデル)動かした時にメモリ使用量は26GBぐらいで頭打ち、M1 Macではかなりスワップが発生してイエローマークだったけど、今のところ32GBあれば何とかなる

② 速度的にはアプリの立ち上げは高速化されている、特にAutodesk Fusionなどを立ち上げるとよくわかる

③ キーボードのタッチ感(音)が説明は難しいけど微妙に異なるんだよね、箱(筐体)が違うせいかも知れない

④ 電池の充電をきっちり80%で打ち切るような設定になっている、M1 Macまでは充電抑止ロジックがよく分からなかったけれど

⑤ M4からでも無いだろうけど、IPhone15は既に切り替わり、ACアダプタのケーブルが柔軟性があるものに変わっているいるから取り回しが楽になっているのとMagSafeは安全面からもおすすめだと思う

普通にM1 Macの資源引き継いで使えるというありきたりの感想、まだファンは一度も回ってないから、ファンが付いていることすら気づいてない

 

admin

目玉モデルを作る(ラズパイ5+カメラ)

視覚相当機能はラズパイ5の標準カメラで実現しますが、ロボットなので目玉風に格納したい

目玉の中心部にカメラ、カメラを覆うような形でサーボモーターで瞼を動かしてみる

<瞼側>

<眼球側>

サーボモーターを取り付け部分はそのままだと造形できないので、仮にサポートを立てて、後でハンダゴテで溶かしてニッパーで切り落とし、最初からニッパーだとストレスで割れます

 

<カメラ取り付け>

<カメラの外観>

<可動部分>

<動作の動画>

マイムービー – SD 480p

カメラからの画像をどう扱うかはこれから、YOLOとか有力だと思うけど

 

<サーボモーター駆動のコード>

PWM機能はgpiodにないのでソフトでPWM作成

import gpiod
import time

CHIP = 'gpiochip4'  # Raspberry Pi 5では'gpiochip4'を使用
PIN = 18  

chip = gpiod.Chip(CHIP)
line = chip.get_line(PIN)
line.request(consumer="Servo", type=gpiod.LINE_REQ_DIR_OUT)

def set_servo_angle(angle):
    duty_cycle = (angle / 18) + 2.5
    pulse_width = duty_cycle / 100 * 20000  # 20ms周期

    line.set_value(1)
    time.sleep(pulse_width / 1000000)
    line.set_value(0)
    time.sleep((20000 - pulse_width) / 1000000)

try:
    while True:
        for angle in range(80,141, 5):
            for _ in range(10):  # 各角度でn回パルスを送信
                set_servo_angle(angle)
            time.sleep(0.1)
except KeyboardInterrupt:
    pass
finally:
    line.release()

 

admin

 

Mac OSでもWSL相当機能(Lima)があるんだ、

Rancher Desktopを起動しているときにQEMUがメモリを4GB程度消費するので代替え手段あるかと調べてみたら、Rancher DesktopではQEMU以外にmacOSの標準機能であるVirtualization.Frameworkというのがあるようです、その名の通りでMac上でLinuxのVMを動作させることができます

Rancher  DesktopではQEMUとVZ(Virtualization.Framework)の選択ができるようになっていて、

これは切り替えた状態、メモリ消費量(2.7GBとかになってます)は多少緩和されてる模様、ただし速度的にはmacOS nativeの機能だろうから高速化は期待できそうです

WSL相当(使ったことはないけど)ならば、個人の使用でそこそこVMwareの代替えとして使えそうです

多少紛らわしいのですが、Rancher DesktopではLimaを直接呼んでいるわけではなくて、仮想化のためにQEMUあるいはVZを選択しているだけで、Lima自体に仮想化の機能は持たないので、実際の仮想化処理はLimaからQEMUもしくはVZを呼び出します

MacでLimaをインストするならば、

% brew install lima

helpで見てみるとLimaはlimactlのaliasだよと言われます、その名の通りですね

% lima --help
Usage: lima [COMMAND...]

lima is an alias for "limactl shell default".

 

admin

M1 MacBook Air 13インチからM4 MacBook Pro 14インチに、

M1 MacBook Air(メモリ:16GB/SSD:512GB)もSSD容量(画像とか動画はほぼないからツール類が大半)が残り約20%、メモリもメモリプレッシャー(コンテナとFusionがリソースを消費する)が画像のように出始めたので、アップデート

Airで32GBモデルはないので、M4ノーマルでMacBook Pro 14インチでメモリ32GB、SSD 1Tにしました、キーボードはいつもの通りUSキーボードの選択、CTOはメモリ容量(+8GB)とキーボード(US)の2点だけ

従来のMacBook Proと比較すると、HDMIとSDカードが直挿しできるのは何気に便利に感じます、ACアダプタもMagSafeの方が安心できると思う、その分犠牲になるはデザイン性なのだけれども、ボディカラーがスペースブラックというのはデフォルトカラーらしいとオーダーしてから気づきましたが、印象は現物見ないと判断できない

この程度の能力では、まだ生成A.Iをクライアントで動かすには、作成済みのモデルを使うならともかく、学習には全く非力だから計算能力が高まれば、また新たなアプリケーションが生まれていきます

この構成でも数年後には次のモデルに変えるのだろうと思う

 

admin

ラズパイ5のWi-Fi接続遅い問題の解決(SSIDの接続優先順位指定)

タイトル通りですが、やることは二つ

一つ目はLocalを日本に設定して5Gが使えるようにすること

二つ目は複数のSSIDを使っている時に(今回はnaとn接続の場合には)acのSSIDの優先順位を高く設定する

この二つですが、ロケール設定はraspy-configで、優先順位指定は最近のラズパイOSでは設定ファイル編集ではなく、nmcli(おそらくnatwork manager by command line interfaceの略)を使うようです

以下、二つのSSIDの場合の設定例です

設定(数字が大きい方を優先する、デフォルトは0らしい)
$ sudo nmcli connection modify "preconfigured" connection.autoconnect-priority 100

確認方法
$ nmcli connection show "preconfigured" | grep connection.autoconnect-priority

connection.autoconnect-priority:        100

$ nmcli connection show "他のssid” | grep connection.autoconnect-priority

connection.autoconnect-priority:        0

この場合、デフォルト設定の方がac接続なのでそこの優先順位を上げました、これで設定を有効(リブートあるいはnmcliで指定)すれば大丈夫です

ブラウザからの速度確認で、n接続では30Mbps程度でしたが、この程度の速度になりました、ac接続としてはそれほど高速でもないですが、相対的にはかなり速くはなってます

 

admin

ローカルマシンでLLMを使ってみる

軽量のプラットホームであれば、パソコンでも動作可能とのことなので、M1 Mac(16GB)でポピュラーと思われるOllamaを動かしてみました、メモリは16GBはないと動きません、遅くても良いからとラズパイ5(8GB)で動かそうとしたらメモリ不足で動きませんでした

インスト方法は、モデルの変換も記述されています

https://qiita.com/s3kzk/items/3cebb8d306fb46cabe9f

OllamaはLLMのフレームワークなので実際に使うためにはモデルのインストが必要になります

<コマンドラインでのやり取りの例>

起動方法は、

% ollama run elyza:jp8b

一方APIを使う場合には、

% ollama serve 

でollamaを起動しておいて、

https://highreso.jp/edgehub/machinelearning/ollamapython.html

を参考にスクリプトを作成して、

import requests
import json

url = 'http://localhost:11434/api/chat'

data = {
    "model":"elyza:jp8b",
    "messages": [
        {
            "role": "user",
            "content": "千葉県の名産品を教えて、"
        }
    ],
    "stream": False
}

response = requests.post(url, data=json.dumps(data))
response_data = response.json()
print(response_data)

このスクリプトを実行すると、

のようなjson形式のレスポンスが返ってきます、コンソールの対話モードに比較するとMacが考えている時間がかなり長い、おそらく10秒ちょっとかな、

リソースの消費状況は、

 

こんな感じなので、やはりラズパイ5では実用上は無理かな、

 

admin

 

ラズパイ5でgpioを扱う

ラズパイ5で大きなハードウェア変更があり、結果としてgpioを扱うライブラリも変更があったようです、一番大きな変更はcやPythonでgpio扱う時にはroot権限でないと動作しないということかもしれません

プロービングしやすいgpioの21ピンを対象にして、動かしてみました

これはPythonのコード、10ms毎にステートを0,1 繰り返しているだけ

こんな当たり前の波形

次にRustでもやってみた、以下がコードですが終了しないので最後のok(())はコメントアウト

実はスレッドの起動と終了待ちには結構時間かかってそれだけで50μs近くかかるから、時間待ちに数μs以下を指定しても無味

これは1μs時間待ちのケース

そしてこれはスレッド起動をコメントアウトしたもの

およそ50nsぐらいのパルスになっています、オシロのサンプリング周波数が40MHzぐらいなので、帯域が追いついてないですが

これでもラズピコのgpio制御に比較すると遅いので、組み込み系にはラズピコの方が向いているということなんだろうと思う

 

admin

 

 

 

 

Geminiへのリクエストを音声でやりとりするようにしてみた

音声認識、LLMへのリクエストとレスポンス、text2speechを一連の流れで実行できるようにしてみた

最初はMacでやったけれども、数箇所手直しするだけでラズパイ5でもちゃんと動作、正常系だけなのでユーザエクスペリエンス的にはまだまだ改善必要ですが、

三本のコードのマージはPerplexityで実行させてます、GeminiにPythonからアクセスするためにAPIキーが必要になりますが、以下のリンクから今は無償で取得できます、APIキーはシステム環境変数に保存、他人の資産だからそれはオープンにはできない

https://aistudio.google.com/apikey

<全体のコード>

import vosk
import pyaudio
import json
import numpy as np
import sounddevice as sd
import queue
import threading
import time
import os
from dotenv import load_dotenv
import google.generativeai as genai
import subprocess

class VoskSpeechRecognizer:
    def __init__(self, model_path='./vrecog/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',
            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}")
                        response_text = query_gemini(text)  # Gemini APIに問い合わせる
                        jtalk(response_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 query_gemini(prompt):
    """
    Gemini APIに問い合わせて応答を取得する関数。
    """
    try:
        response = model.generate_content(prompt)
        print(f"Gemini応答: {response.text}")
        return response.text.strip()
    except Exception as e:
        print(f"Gemini APIエラー: {e}")
        return "エラーが発生しました。もう一度試してください。"

def jtalk(text):
    """
    Open JTalkでテキストを音声合成し再生する関数。
    """
    open_jtalk = ['/usr/bin/open_jtalk']
    mech = ['-x', '/var/lib/mecab/dic/open-jtalk/naist-jdic']
    htsvoice = ['-m', '/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice']
    speed = ['-r', '1.0']
    outwav = ['-ow', 'out.wav']
    cmd = open_jtalk + mech + htsvoice + speed + outwav
    
    try:
        proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
        proc.stdin.write(text.encode('utf-8'))
        proc.stdin.close()
        proc.wait()
        
        # 音声ファイルを再生する場合
        subprocess.call(['aplay', 'out.wav'])
    except Exception as e:
        print(f"音声合成エラー: {e}")

def main():
    # 環境変数からGoogle APIキーを読み込む
    load_dotenv()
    GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
    
    if not GOOGLE_API_KEY:
        print("Google APIキーが設定されていません。")
        return
    
    # Google Gemini APIの設定
    genai.configure(api_key=GOOGLE_API_KEY, transport="rest")
    
    global model  # グローバル変数としてモデルを定義(query_geminiで使用)
    model = genai.GenerativeModel("gemini-1.5-flash")
    
    recognizer = VoskSpeechRecognizer()
    
    try:
        print("音声認識を開始します。Ctrl+Cで終了できます。")
        recognizer.start_recognition()
        
        # 無限ループを防ぐ(Ctrl+Cで停止可能)
        while True:
            time.sleep(1)
    
    except KeyboardInterrupt:
        print("\n音声認識を終了します...")
    finally:
        recognizer.stop_recognition()

if __name__ == "__main__":
    main()

<動作例>

jtalkは返答の最初のブロックしか読み上げないようだけれども、長いレスポンスを全部読み上げられてもというところだから、最初だけで十分かもしれない

Geminiはマルチモーダルなので、音声認識や合成もクラウドでできそうですが、そこまでクラウドにアップロードは躊躇われるので、端末側で処理するのが妥当じゃないかと今は考えています

 

admin

ラズパイ標準カメラV3

カメラはラズパイ標準を使ってみる、

https://www.switch-science.com/products/9933?_pos=5&_sid=3c0939413&_ss=r

オートフォーカスで、ラズパイ専用は特に設定も不要で動く

カメラの見え方は、

$ rpicam-hello --list-cameras
Available cameras
-----------------
0 : imx708 [4608x2592 10-bit RGGB] (/base/axi/pcie@120000/rp1/i2c@88000/imx708@1a)
    Modes: 'SRGGB10_CSI2P' : 1536x864 [120.13 fps - (768, 432)/3072x1728 crop]
                             2304x1296 [56.03 fps - (0, 0)/4608x2592 crop]
                             4608x2592 [14.35 fps - (0, 0)/4608x2592 crop]

物の梱包状態、

FPCは表裏(つまりコネクタの片側にしか接点はない)があるので、間違えないように、写真を見ればわかりますが

コマンド系にはlibcameraとrpicamがありますが、どちらでも動きますが今後はrpicamが標準になっていくらしいのでこちらを使うべき、以下はjpegで取り込む例です

$ libcamera-jpeg -o test.jpg -t 2000 --width 800 --height 600
  or
$ rpicam-jpeg -o test.jpg -t 2000 --width 800 --height 600

なぜかVNC経由ではフレームレートが1fps以下の極遅モードになりますが、対策方法はヘッドレス(モニターレス)で使われるダミーのHDMIをつなぐのが一番スマートかもしれない、設定は上手くいかなかった、まあデスクトップは最終的に使わないから

表示だけが遅くなるのであって、録画した動画をMacに転送すれば普通に30fpsとかで再生できる、つまりちゃんと録画はされているからラズパイ- VNC転送時の問題です

 

admin