PersonaPlex
โมเดล speech-to-speech บทสนทนาแบบฟูล-ดูเพล็กซ์ ที่ใช้สถาปัตยกรรม Moshi (Kyutai) PersonaPlex 7B สร้างคำตอบที่พูดได้โดยตรงจากอินพุตที่เป็นเสียงพูด — ไม่จำเป็นต้องมี pipeline ข้อความตรงกลาง โมเดลมาพร้อมพรีเซ็ตเสียง 18 ตัวและพร้อมใช้ในรูปแบบที่ลดความละเอียดเป็น 8-bit (แนะนำ) และ 4-bit 8-bit เป็นค่าเริ่มต้น — เร็วกว่า 30% และสร้างคำตอบที่สอดคล้องกัน ในขณะที่ 4-bit จะลดคุณภาพเอาต์พุต
สถาปัตยกรรม
PersonaPlex เป็นโมเดล autoregressive แบบหลายสตรีมที่มีส่วนประกอบหลัก 3 ส่วน:
| ส่วนประกอบ | รายละเอียด |
|---|---|
| Temporal Transformer | 32 layers, dim=4096, 32 heads, SwiGLU (hidden_scale=4.125), RoPE, 8-bit quantized (default) |
| Depformer | 6 layers, dim=1024, 16 heads, MultiLinear (weights_per_step=true), dep_q=16 |
| Mimi Codec | 16 codebooks, 12.5 Hz frame rate, 24 kHz audio output |
โมเดลประมวลผล 17 สตรีม พร้อมกัน: 1 สตรีมข้อความ + 8 สตรีมเสียงผู้ใช้ + 8 สตรีมเสียงเอเจนต์ สถาปัตยกรรมนี้รองรับบทสนทนาแบบฟูล-ดูเพล็กซ์ที่โมเดลสามารถฟังและพูดได้ในเวลาเดียวกัน
พรีเซ็ตเสียง
PersonaPlex มาพร้อมพรีเซ็ตเสียง 18 ตัวในสไตล์ที่เป็นธรรมชาติและหลากหลาย:
| หมวดหมู่ | พรีเซ็ต |
|---|---|
| หญิงธรรมชาติ | NATF0, NATF1, NATF2, NATF3 |
| ชายธรรมชาติ | NATM0, NATM1, NATM2, NATM3 |
| หญิงหลากหลาย | VARF0, VARF1, VARF2, VARF3, VARF4 |
| ชายหลากหลาย | VARM0, VARM1, VARM2, VARM3, VARM4 |
ความคิดภายใน (Inner Monologue)
PersonaPlex สร้างสตรีมขนานสองสตรีมในทุกขั้นตอน: 8 codebook token เสียงสำหรับ codec Mimi และ 1 text token สำหรับ inner monologue ของโมเดล สตรีมข้อความคือสิ่งที่โมเดล “กำลังคิด” ขณะพูด — อาจเบี่ยงเบนเล็กน้อยจากเสียงสุดท้าย แต่ในทางปฏิบัติจะสะท้อนคำตอบที่พูดได้ใกล้พอที่จะใช้เป็นบทถอดความสด
Text token จะกลับมาเป็น raw SentencePiece piece ID ถอดรหัสด้วย SentencePieceDecoder ที่ PersonaPlex ปล่อยมาด้วย:
import PersonaPlex
import AudioCommon
let model = try await PersonaPlexModel.fromPretrained()
let decoder = try model.makeTextDecoder() // SentencePieceDecoder
let result = model.respondWithTranscript(userAudio: userSamples, voice: .NATM0)
let transcript = decoder.decode(result.textTokens)
print(transcript) // "Sure, I can help with that..."
playAudio(result.audio) // 24 kHz mono Float32
ในโหมด streaming, respondStream จะปล่อย textTokens chunk ทีละชุดเมื่อถูกสร้างขึ้น — ถอดรหัสทีละขั้นเพื่อขับเคลื่อนหน้า caption แบบสดในขณะที่เสียงยังคงสร้างอยู่ แฟล็ก --transcript CLI ทำสิ่งนี้เบื้องหลังพอดี
เหตุใดเรื่องนี้จึงสำคัญ: SentencePieceDecoder สร้างขึ้นบน protobuf reader ของ AudioCommon.SentencePieceModel ที่ใช้ร่วมกัน ดังนั้น PersonaPlex, OmnilingualASR และโมเดลที่ใช้ SentencePiece อนาคตจึงถอดรหัสผ่าน tokenizer แบบเดียวกัน ดู เอกสารอ้างอิง SentencePieceModel
System Prompt
ส่ง system prompt แบบกำหนดเองใด ๆ เป็นสตริงธรรมดา — ไม่จำเป็นต้องใช้ tokenization ภายนอก:
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
หรือใช้พรีเซ็ตในตัว:
assistant— ผู้ช่วยอเนกประสงค์ที่เป็นประโยชน์ (ค่าเริ่มต้น)focused— คำตอบที่กระชับและตรงไปตรงมาcustomer-service— เจ้าหน้าที่สนับสนุนที่สุภาพและเน้นการแก้ปัญหาteacher— สไตล์การสอนที่อดทนและอธิบายชัดเจน
การใช้งาน CLI
สร้างคำตอบเป็นเสียงพูดจากอินพุตเสียง:
# speech-to-speech พื้นฐาน
.build/release/speech respond --input question.wav
# เลือกพรีเซ็ตเสียง
.build/release/speech respond --input question.wav --voice NATM0
# สตรีมเอาต์พุตเสียงระหว่างการสร้าง
.build/release/speech respond --input question.wav --stream
# ข้อความ system prompt แบบกำหนดเอง
.build/release/speech respond --input question.wav --system-prompt-text "You enjoy having a good conversation."
# ใช้พรีเซ็ต system prompt
.build/release/speech respond --input question.wav --system-prompt customer-service
# รับบทถอดความพร้อมกับเสียง
.build/release/speech respond --input question.wav --transcript
# เอาต์พุต JSON พร้อม metadata
.build/release/speech respond --input question.wav --json
ตัวเลือก
| ตัวเลือก | คำอธิบาย |
|---|---|
--input | ไฟล์เสียงอินพุต (WAV, ต้องระบุ) |
--voice | ชื่อพรีเซ็ตเสียง (เช่น NATM0, VARF2) |
--system-prompt | พรีเซ็ต system prompt: assistant, focused, customer-service, teacher |
--system-prompt-text | ข้อความ system prompt แบบกำหนดเอง (เขียนทับ --system-prompt) |
--max-steps | ขั้นตอนการสร้างสูงสุด |
--stream | ปล่อย chunk เสียงระหว่างการสร้าง |
--compile | ใช้ MLX compiled inference เพื่อการสร้างที่เร็วขึ้น |
--transcript | เอาต์พุตบทถอดความข้อความพร้อมกับเสียง |
--json | เอาต์พุต JSON พร้อม metadata |
พารามิเตอร์การสุ่มสามารถเขียนทับได้:
| ตัวเลือก | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|
--audio-temp | 0.8 | อุณหภูมิการสุ่ม audio token |
--audio-top-k | 250 | การสุ่ม top-k ของ audio token |
--text-temp | 0.7 | อุณหภูมิการสุ่ม text token |
--text-top-k | 25 | การสุ่ม top-k ของ text token |
Streaming
แฟล็ก --stream เปิดใช้งานเอาต์พุตเสียงแบบเรียลไทม์ Chunk เสียงจะถูกปล่อยออกมาเมื่อถูกสร้างขึ้น ดังนั้นการเล่นจึงเริ่มต้นได้ก่อนที่คำตอบจะเสร็จสมบูรณ์ มีประโยชน์อย่างยิ่งสำหรับแอปพลิเคชันแบบโต้ตอบที่หน่วงต่ำเป็นสิ่งสำคัญ
ประสิทธิภาพ
| เมตริก | ค่า |
|---|---|
| Real-time factor (RTF) | ~1.4 (8-bit, ใกล้เรียลไทม์) |
| หน่วงของขั้นตอน | ~112 ms/step บน M2 Max (8-bit) |
| ขนาดโมเดล (8-bit) | ~9.1 GB |
| RAM สูงสุด (8-bit) | ~11 GB |
| ขนาดโมเดล (4-bit) | ~4.9 GB |
| RAM สูงสุด (4-bit) | ~7 GB |
PersonaPlex 7B (8-bit) ต้องการ RAM อย่างน้อย 24 GB รุ่น 4-bit พอดีบนอุปกรณ์ 16 GB แต่ผลลัพธ์จะเสื่อมคุณภาพ บนอุปกรณ์ 8 GB จะไม่มีรุ่นใดพอดี ใช้ --compile เพื่อประสิทธิภาพที่ดีที่สุดบนฮาร์ดแวร์ที่รองรับ
รุ่นย่อยของโมเดล
| โมเดล | ขนาด | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8-bit) แนะนำ | 9.1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4-bit) | 4.9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
API Swift
import PersonaPlex
let model = try await PersonaPlexModel.loadFromHub()
let response = try await model.respond(
audioFile: "question.wav",
voice: .NATM0,
systemPrompt: .assistant
)
try response.audio.write(to: "answer.wav")