Qwen3-ASR-0.6B语音识别实战:基于Python的音频处理与转写

你是不是也遇到过这样的场景:手头有一段会议录音需要整理成文字,或者想给一段视频自动生成字幕,但找了一圈工具,要么收费不菲,要么识别不准,尤其是遇到带点口音或者背景音的情况,效果就更差了。

最近我试了试通义千问团队新出的Qwen3-ASR-0.6B模型,感觉挺不错的。它是个专门做语音识别的模型,支持中文、英文、粤语等好几十种语言和方言,而且最吸引我的一点是,它可以在本地运行,不用把音频传到别人的服务器,隐私方面比较放心。

今天这篇文章,我就带你从零开始,用Python把这个模型跑起来,完成从音频文件到文字转录的整个过程。我会把每一步都讲清楚,包括怎么准备环境、怎么处理音频、怎么调用模型,还会分享一些实际使用中遇到的问题和解决办法。

1. 准备工作:环境搭建与模型获取

在开始写代码之前,我们需要先把运行环境准备好。Qwen3-ASR-0.6B支持两种后端:Transformers后端和vLLM后端。Transformers后端比较通用,vLLM后端速度更快,特别是处理大批量音频的时候。这里我们先从简单的Transformers后端开始。

1.1 创建Python虚拟环境

我建议先创建一个独立的Python环境,这样可以避免和你电脑上已有的包产生冲突。用conda或者venv都可以,我这里用conda演示:

# 创建名为qwen3-asr的Python 3.12环境
conda create -n qwen3-asr python=3.12 -y

# 激活环境
conda activate qwen3-asr

如果你没有conda,用Python自带的venv也行:

# 创建虚拟环境
python -m venv qwen3-asr-env

# 激活环境(Linux/macOS)
source qwen3-asr-env/bin/activate

# 激活环境(Windows)
qwen3-asr-env\Scripts\activate

1.2 安装必要的包

接下来安装qwen-asr包,这是官方提供的Python包,里面包含了运行模型需要的所有东西:

# 安装qwen-asr包(Transformers后端)
pip install -U qwen-asr

如果你想用更快的vLLM后端,可以这样安装:

# 安装vLLM后端版本
pip install -U qwen-asr[vllm]

1.3 获取模型文件

模型文件有两种获取方式:通过Hugging Face或者通过ModelScope。如果你在国内,用ModelScope下载速度会快一些。

方式一:通过ModelScope下载(推荐国内用户)

# 先安装modelscope
pip install -U modelscope

# 下载模型
modelscope download --model Qwen/Qwen3-ASR-0.6B --local_dir ./Qwen3-ASR-0.6B

方式二:通过Hugging Face下载

# 先安装huggingface_hub
pip install -U "huggingface_hub[cli]"

# 下载模型
huggingface-cli download Qwen/Qwen3-ASR-0.6B --local-dir ./Qwen3-ASR-0.6B

下载完成后,你会在当前目录下看到一个Qwen3-ASR-0.6B文件夹,里面就是模型文件了。整个模型大约1.9GB,需要一点时间下载。

2. 基础概念:Qwen3-ASR能做什么?

在开始写代码之前,我们先简单了解一下Qwen3-ASR-0.6B这个模型的特点,这样用起来心里更有数。

多语言支持:这个模型支持52种语言和方言,包括中文、英文、粤语、日语、韩语等等。它还能自动识别音频是哪种语言,不用你手动指定。

本地运行:所有计算都在你自己的电脑上完成,音频数据不会上传到任何服务器,对于处理敏感内容或者注重隐私的场景很友好。

适应性强:官方测试显示,即使在有噪音的环境里,或者说话带口音,它的识别效果也还不错。还能处理唱歌的声音和带背景音乐的音轨。

两种推理模式:支持离线推理(一次性处理整个音频文件)和流式推理(边录边识别,适合实时转写)。

时间戳功能:如果配合Qwen3-ForcedAligner-0.6B模型使用,还能输出每个词或字的时间戳,做字幕的时候特别有用。

了解这些特点后,我们就能更好地决定怎么使用它了。比如,如果你要做实时字幕,就用流式推理;如果需要精确的时间对齐,就加上时间戳模型。

3. 第一个例子:快速转录音频文件

现在环境准备好了,模型也下载了,我们来写第一个简单的例子。这个例子会演示怎么用Qwen3-ASR-0.6B转录一个网络上的音频文件。

创建一个Python文件,比如叫first_demo.py,然后写入以下代码:

import torch
from qwen_asr import Qwen3ASRModel

# 加载模型
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",  # 模型名称,如果下载到本地也可以用本地路径
    dtype=torch.bfloat16,    # 使用bfloat16精度,节省内存
    device_map="cuda:0",     # 使用GPU,如果是CPU就改成"cpu"
    max_inference_batch_size=32,  # 推理时的最大批处理大小
    max_new_tokens=256,      # 生成的最大token数,长音频可以设大一点
)

# 转录音频
results = model.transcribe(
    audio="https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-ASR-Repo/asr_en.wav",
    language=None,  # 设为None让模型自动检测语言,也可以指定如"English"
)

# 打印结果
print(f"检测到的语言: {results[0].language}")
print(f"转录文本: {results[0].text}")

运行这个脚本:

python first_demo.py

如果一切正常,你会看到类似这样的输出:

检测到的语言: English
转录文本: This is a test audio for speech recognition.

是不是很简单?我们只用了几行代码就完成了一个语音识别任务。这里有几个关键点需要注意:

  1. 设备选择:如果你有NVIDIA显卡,强烈建议用GPU(device_map="cuda:0"),速度会快很多。如果没有GPU,改成device_map="cpu"也能跑,只是慢一些。

  2. 精度设置dtype=torch.bfloat16表示用半精度浮点数,这样能减少内存占用。如果你的显卡比较老不支持bfloat16,可以改成torch.float16

  3. 音频输入audio参数可以直接传一个网络URL,也可以传本地文件路径,比如audio="./my_audio.wav"

  4. 语言设置language=None让模型自动检测语言。如果你明确知道音频是什么语言,可以指定,比如language="Chinese",这样识别准确率可能会更高。

4. 处理本地音频文件

实际工作中,我们更多是处理本地的音频文件。下面我写一个更实用的例子,展示怎么处理本地文件,并且一次处理多个文件。

4.1 单个本地文件转录

import torch
from qwen_asr import Qwen3ASRModel

# 加载模型
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",
    dtype=torch.bfloat16,
    device_map="cuda:0",
)

# 转录本地音频文件
results = model.transcribe(
    audio="./meeting_recording.wav",  # 本地文件路径
    language=None,
)

print(f"文件: meeting_recording.wav")
print(f"语言: {results[0].language}")
print(f"内容: {results[0].text}")

# 保存结果到文件
with open("./transcription_result.txt", "w", encoding="utf-8") as f:
    f.write(f"语言: {results[0].language}\n")
    f.write(f"内容: {results[0].text}\n")

4.2 批量处理多个文件

如果你有一堆音频文件需要处理,用批处理效率会高很多:

import torch
from qwen_asr import Qwen3ASRModel
import os

# 加载模型
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",
    dtype=torch.bfloat16,
    device_map="cuda:0",
    max_inference_batch_size=8,  # 批处理大小,根据GPU内存调整
)

# 假设音频文件都在audio_files文件夹里
audio_folder = "./audio_files"
audio_files = [f for f in os.listdir(audio_folder) if f.endswith(('.wav', '.mp3', '.flac'))]
audio_paths = [os.path.join(audio_folder, f) for f in audio_files]

if audio_paths:
    # 批量转录
    results = model.transcribe(
        audio=audio_paths,
        language=None,  # 可以传列表指定每个文件的语言,或者用None自动检测
    )
    
    # 输出所有结果
    for i, result in enumerate(results):
        print(f"\n文件: {audio_files[i]}")
        print(f"语言: {result.language}")
        print(f"内容: {result.text[:100]}...")  # 只打印前100字符
        
        # 每个结果保存到单独的文件
        output_file = f"./transcriptions/{audio_files[i]}_transcript.txt"
        os.makedirs(os.path.dirname(output_file), exist_ok=True)
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(f"文件: {audio_files[i]}\n")
            f.write(f"语言: {result.language}\n")
            f.write(f"内容: {result.text}\n")
else:
    print("没有找到音频文件")

这个批量处理的例子有几个实用技巧:

  1. 文件格式支持:代码里支持.wav、.mp3、.flac等常见格式,你可以根据实际情况调整。

  2. 批处理大小max_inference_batch_size控制一次处理多少个文件。如果你的GPU内存不大(比如8GB),建议设为4或8;如果内存大(比如24GB),可以设大一点。

  3. 结果保存:每个音频的转录结果保存到单独的文件,方便后续处理。

5. 高级功能:时间戳与流式识别

除了基本的转录功能,Qwen3-ASR还有一些高级功能,在做特定任务时特别有用。

5.1 获取时间戳(做字幕用)

如果你需要给视频加字幕,或者想精确知道每个词在音频里的位置,就需要时间戳功能。这需要额外加载一个时间戳对齐模型:

import torch
from qwen_asr import Qwen3ASRModel

# 加载主模型和时间戳模型
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",
    dtype=torch.bfloat16,
    device_map="cuda:0",
    forced_aligner="Qwen/Qwen3-ForcedAligner-0.6B",  # 时间戳模型
    forced_aligner_kwargs=dict(
        dtype=torch.bfloat16,
        device_map="cuda:0",
    ),
)

# 转录并获取时间戳
results = model.transcribe(
    audio="https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-ASR-Repo/asr_zh.wav",
    language="Chinese",  # 时间戳功能需要指定语言
    return_time_stamps=True,  # 关键参数:返回时间戳
)

# 输出结果
print(f"语言: {results[0].language}")
print(f"文本: {results[0].text}")
print("\n时间戳详情:")

# 打印每个词的时间戳
for segment in results[0].time_stamps:
    for item in segment:
        # item.text: 词或字
        # item.start_time: 开始时间(秒)
        # item.end_time: 结束时间(秒)
        print(f"  '{item.text}': {item.start_time:.2f}s - {item.end_time:.2f}s")

# 生成SRT字幕格式
def generate_srt(time_stamps):
    srt_content = ""
    for i, segment in enumerate(time_stamps, 1):
        for j, item in enumerate(segment):
            # 将秒转换为SRT时间格式:HH:MM:SS,mmm
            start_h = int(item.start_time // 3600)
            start_m = int((item.start_time % 3600) // 60)
            start_s = int(item.start_time % 60)
            start_ms = int((item.start_time - int(item.start_time)) * 1000)
            
            end_h = int(item.end_time // 3600)
            end_m = int((item.end_time % 3600) // 60)
            end_s = int(item.end_time % 60)
            end_ms = int((item.end_time - int(item.end_time)) * 1000)
            
            srt_content += f"{i}\n"
            srt_content += f"{start_h:02d}:{start_m:02d}:{start_s:02d},{start_ms:03d} --> "
            srt_content += f"{end_h:02d}:{end_m:02d}:{end_s:02d},{end_ms:03d}\n"
            srt_content += f"{item.text}\n\n"
    return srt_content

srt_output = generate_srt(results[0].time_stamps)
with open("./subtitle.srt", "w", encoding="utf-8") as f:
    f.write(srt_output)
print("SRT字幕文件已生成: subtitle.srt")

时间戳功能在做视频字幕、音频标注、语音分析等场景特别有用。生成的字幕文件可以直接导入到剪辑软件里使用。

5.2 流式识别(实时转写)

如果你需要做实时语音转写,比如会议直播字幕、实时翻译等,就需要用流式识别。流式识别是边录音边识别,不用等整个音频录完。

import numpy as np
import soundfile as sf
from qwen_asr import Qwen3ASRModel

# 注意:流式识别目前只支持vLLM后端
# 需要先安装:pip install -U qwen-asr[vllm]

def simulate_streaming(audio_file, chunk_duration_ms=1000):
    """模拟流式输入,将音频文件切成小块逐步输入"""
    
    # 加载vLLM后端模型
    model = Qwen3ASRModel.LLM(
        model="Qwen/Qwen3-ASR-0.6B",
        gpu_memory_utilization=0.7,
        max_new_tokens=32,  # 流式识别设小一点
    )
    
    # 读取音频文件
    audio_data, sample_rate = sf.read(audio_file)
    
    # 确保是单声道和16kHz采样率(模型要求)
    if len(audio_data.shape) > 1:
        audio_data = audio_data[:, 0]  # 取左声道
    
    if sample_rate != 16000:
        # 简单重采样到16kHz
        duration = len(audio_data) / sample_rate
        new_length = int(duration * 16000)
        audio_data = np.interp(
            np.linspace(0, len(audio_data)-1, new_length),
            np.arange(len(audio_data)),
            audio_data
        )
        sample_rate = 16000
    
    # 初始化流式状态
    state = model.init_streaming_state(
        unfixed_chunk_num=2,
        unfixed_token_num=5,
        chunk_size_sec=2.0,
    )
    
    # 计算每个块的长度
    chunk_size = int(chunk_duration_ms / 1000 * sample_rate)
    
    print("开始流式识别...")
    print("-" * 50)
    
    # 模拟流式输入
    position = 0
    chunk_count = 0
    
    while position < len(audio_data):
        # 取一个音频块
        end_pos = min(position + chunk_size, len(audio_data))
        audio_chunk = audio_data[position:end_pos]
        
        # 流式识别
        model.streaming_transcribe(audio_chunk, state)
        
        chunk_count += 1
        current_time = position / sample_rate
        
        print(f"[{current_time:.1f}s] 片段{chunk_count}:")
        print(f"  当前识别: {state.text}")
        print(f"  检测语言: {state.language}")
        print()
        
        position = end_pos
    
    # 结束流式识别
    model.finish_streaming_transcribe(state)
    
    print("-" * 50)
    print("识别完成!")
    print(f"最终文本: {state.text}")
    print(f"最终语言: {state.language}")
    
    return state.text

# 使用示例
if __name__ == "__main__":
    # 模拟流式识别一个本地文件
    result = simulate_streaming("./test_audio.wav", chunk_duration_ms=500)

流式识别的关键是init_streaming_state创建状态对象,然后不断用streaming_transcribe输入新的音频数据,最后用finish_streaming_transcribe结束。这种方式延迟低,适合实时场景。

6. 实际应用中的问题与解决

在实际使用中,你可能会遇到一些问题。下面我总结了一些常见问题和解决办法。

6.1 内存不足问题

Qwen3-ASR-0.6B虽然比1.7B版本小,但在一些显卡上还是可能遇到内存不足的问题。可以尝试这些方法:

# 方法1:使用CPU(慢,但肯定能跑)
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",
    dtype=torch.float32,  # CPU上可以用float32
    device_map="cpu",     # 关键:用CPU
)

# 方法2:降低批处理大小
model = Qwen3ASRModel.from_pretrained(
    "Qwen/Qwen3-ASR-0.6B",
    dtype=torch.float16,  # 用float16节省内存
    device_map="cuda:0",
    max_inference_batch_size=2,  # 减小批处理大小
)

# 方法3:分割长音频
def transcribe_long_audio(audio_path, chunk_duration=30):
    """将长音频切成小段处理"""
    import librosa
    
    # 加载音频
    audio, sr = librosa.load(audio_path, sr=16000)
    
    # 计算每段的样本数
    chunk_samples = chunk_duration * sr
    
    full_text = ""
    for i in range(0, len(audio), chunk_samples):
        chunk = audio[i:i+chunk_samples]
        
        # 临时保存chunk到文件
        temp_file = f"temp_chunk_{i//chunk_samples}.wav"
        sf.write(temp_file, chunk, sr)
        
        # 转录这个chunk
        results = model.transcribe(audio=temp_file)
        full_text += results[0].text + " "
        
        # 清理临时文件
        os.remove(temp_file)
    
    return full_text

6.2 音频格式问题

模型对音频格式有一定要求,如果遇到识别效果差,可以试试预处理:

def preprocess_audio(input_path, output_path):
    """预处理音频:转单声道、16kHz、WAV格式"""
    import librosa
    
    # 加载音频
    audio, sr = librosa.load(input_path, sr=None, mono=False)
    
    # 转单声道(如果是立体声)
    if len(audio.shape) > 1:
        audio = librosa.to_mono(audio)
    
    # 重采样到16kHz
    if sr != 16000:
        audio = librosa.resample(audio, orig_sr=sr, target_sr=16000)
    
    # 保存为WAV格式
    sf.write(output_path, audio, 16000)
    
    print(f"预处理完成: {input_path} -> {output_path}")
    print(f"采样率: 16000 Hz, 声道: 单声道")
    
    return output_path

# 使用示例
processed_audio = preprocess_audio("input.mp3", "processed.wav")
results = model.transcribe(audio=processed_audio)

6.3 识别效果优化

如果识别效果不理想,可以尝试这些方法:

  1. 明确指定语言:如果你知道音频的语言,明确指定会比自动检测更准。

  2. 音频质量:确保音频清晰,背景噪音小。可以用降噪工具先处理一下。

  3. 分段处理:特别长的音频(超过10分钟)可以分段处理,每段单独识别。

  4. 调整参数:试试不同的max_new_tokens值,对于长音频可以设大一点。

# 优化后的转录
results = model.transcribe(
    audio=audio_path,
    language="Chinese",  # 明确指定语言
    # 对于长音频,增加max_new_tokens
    # 在模型加载时设置:max_new_tokens=1024
)

7. 完整项目示例:会议录音转文字工具

最后,我结合上面讲的所有内容,写一个完整的会议录音转文字工具。这个工具可以处理一个文件夹里的所有录音文件,生成带时间戳的转录结果,并保存为多种格式。

import os
import torch
import argparse
from datetime import datetime
from qwen_asr import Qwen3ASRModel

class MeetingTranscriber:
    """会议录音转文字工具"""
    
    def __init__(self, model_path="Qwen/Qwen3-ASR-0.6B", use_gpu=True, use_timestamps=True):
        """初始化转录器"""
        
        self.use_gpu = use_gpu and torch.cuda.is_available()
        self.use_timestamps = use_timestamps
        
        print(f"初始化模型...")
        print(f"使用GPU: {self.use_gpu}")
        print(f"使用时间戳: {self.use_timestamps}")
        
        # 准备模型参数
        model_kwargs = {
            "dtype": torch.bfloat16 if self.use_gpu else torch.float32,
            "device_map": "cuda:0" if self.use_gpu else "cpu",
            "max_inference_batch_size": 4,
            "max_new_tokens": 512,
        }
        
        # 如果需要时间戳,加载对齐模型
        if self.use_timestamps:
            model_kwargs.update({
                "forced_aligner": "Qwen/Qwen3-ForcedAligner-0.6B",
                "forced_aligner_kwargs": {
                    "dtype": torch.bfloat16 if self.use_gpu else torch.float32,
                    "device_map": "cuda:0" if self.use_gpu else "cpu",
                }
            })
        
        # 加载模型
        self.model = Qwen3ASRModel.from_pretrained(model_path, **model_kwargs)
        
        print("模型加载完成!")
    
    def transcribe_file(self, audio_path, language=None):
        """转录单个文件"""
        
        print(f"\n处理文件: {os.path.basename(audio_path)}")
        
        try:
            # 转录
            results = self.model.transcribe(
                audio=audio_path,
                language=language,
                return_time_stamps=self.use_timestamps,
            )
            
            result = results[0]
            
            # 打印摘要信息
            print(f"  语言: {result.language}")
            print(f"  文本长度: {len(result.text)} 字符")
            
            if self.use_timestamps and hasattr(result, 'time_stamps'):
                print(f"  时间戳数量: {len(result.time_stamps[0]) if result.time_stamps else 0}")
            
            return {
                "file": audio_path,
                "language": result.language,
                "text": result.text,
                "timestamps": result.time_stamps if self.use_timestamps else None,
                "success": True
            }
            
        except Exception as e:
            print(f"  错误: {str(e)}")
            return {
                "file": audio_path,
                "error": str(e),
                "success": False
            }
    
    def save_results(self, result, output_dir):
        """保存转录结果"""
        
        if not result["success"]:
            return
        
        base_name = os.path.splitext(os.path.basename(result["file"]))[0]
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # 创建输出目录
        os.makedirs(output_dir, exist_ok=True)
        
        # 1. 保存为纯文本
        txt_path = os.path.join(output_dir, f"{base_name}_{timestamp}.txt")
        with open(txt_path, "w", encoding="utf-8") as f:
            f.write(f"文件: {result['file']}\n")
            f.write(f"语言: {result['language']}\n")
            f.write(f"时间: {timestamp}\n")
            f.write("\n" + "="*50 + "\n\n")
            f.write(result["text"])
        
        # 2. 如果有时戳,保存为SRT字幕
        if result["timestamps"]:
            srt_path = os.path.join(output_dir, f"{base_name}_{timestamp}.srt")
            self._save_as_srt(result, srt_path)
        
        # 3. 保存为JSON格式(包含所有信息)
        json_path = os.path.join(output_dir, f"{base_name}_{timestamp}.json")
        self._save_as_json(result, json_path)
        
        print(f"  结果已保存到: {output_dir}")
        return txt_path
    
    def _save_as_srt(self, result, srt_path):
        """保存为SRT字幕格式"""
        
        if not result["timestamps"]:
            return
        
        with open(srt_path, "w", encoding="utf-8") as f:
            for i, segment in enumerate(result["timestamps"], 1):
                for j, item in enumerate(segment):
                    # 转换时间格式
                    start_time = self._seconds_to_srt_time(item.start_time)
                    end_time = self._seconds_to_srt_time(item.end_time)
                    
                    f.write(f"{i}\n")
                    f.write(f"{start_time} --> {end_time}\n")
                    f.write(f"{item.text}\n\n")
    
    def _save_as_json(self, result, json_path):
        """保存为JSON格式"""
        
        import json
        
        data = {
            "file": result["file"],
            "language": result["language"],
            "text": result["text"],
            "timestamp": datetime.now().isoformat(),
        }
        
        if result["timestamps"]:
            data["timestamps"] = [
                {
                    "text": item.text,
                    "start": item.start_time,
                    "end": item.end_time
                }
                for segment in result["timestamps"]
                for item in segment
            ]
        
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
    
    @staticmethod
    def _seconds_to_srt_time(seconds):
        """将秒数转换为SRT时间格式"""
        
        hours = int(seconds // 3600)
        minutes = int((seconds % 3600) // 60)
        secs = int(seconds % 60)
        millis = int((seconds - int(seconds)) * 1000)
        
        return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
    
    def process_folder(self, input_folder, output_folder, language=None):
        """处理整个文件夹的音频文件"""
        
        # 支持的音频格式
        audio_extensions = {'.wav', '.mp3', '.flac', '.m4a', '.ogg'}
        
        # 查找所有音频文件
        audio_files = []
        for root, dirs, files in os.walk(input_folder):
            for file in files:
                if os.path.splitext(file)[1].lower() in audio_extensions:
                    audio_files.append(os.path.join(root, file))
        
        if not audio_files:
            print(f"在 {input_folder} 中没有找到音频文件")
            return []
        
        print(f"\n找到 {len(audio_files)} 个音频文件")
        
        # 处理每个文件
        results = []
        for i, audio_file in enumerate(audio_files, 1):
            print(f"\n[{i}/{len(audio_files)}] ", end="")
            
            result = self.transcribe_file(audio_file, language)
            
            if result["success"]:
                saved_path = self.save_results(result, output_folder)
                result["saved_path"] = saved_path
            
            results.append(result)
        
        # 生成汇总报告
        self._generate_summary(results, output_folder)
        
        return results
    
    def _generate_summary(self, results, output_dir):
        """生成处理汇总报告"""
        
        successful = [r for r in results if r["success"]]
        failed = [r for r in results if not r["success"]]
        
        report_path = os.path.join(output_dir, "processing_summary.txt")
        
        with open(report_path, "w", encoding="utf-8") as f:
            f.write("会议录音转录汇总报告\n")
            f.write("=" * 50 + "\n\n")
            f.write(f"处理时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"总文件数: {len(results)}\n")
            f.write(f"成功: {len(successful)}\n")
            f.write(f"失败: {len(failed)}\n\n")
            
            if successful:
                f.write("成功文件列表:\n")
                for r in successful:
                    f.write(f"  - {os.path.basename(r['file'])} ({r['language']})\n")
                f.write("\n")
            
            if failed:
                f.write("失败文件列表:\n")
                for r in failed:
                    f.write(f"  - {os.path.basename(r['file'])}: {r['error']}\n")
        
        print(f"\n处理完成!")
        print(f"成功: {len(successful)} 个文件")
        print(f"失败: {len(failed)} 个文件")
        print(f"详细报告: {report_path}")

def main():
    """主函数"""
    
    parser = argparse.ArgumentParser(description="会议录音转文字工具")
    parser.add_argument("input", help="输入文件或文件夹路径")
    parser.add_argument("-o", "--output", default="./transcriptions", help="输出文件夹路径")
    parser.add_argument("-l", "--language", help="指定语言(如Chinese、English)")
    parser.add_argument("--no-gpu", action="store_true", help="不使用GPU")
    parser.add_argument("--no-timestamps", action="store_true", help="不生成时间戳")
    
    args = parser.parse_args()
    
    # 创建转录器
    transcriber = MeetingTranscriber(
        use_gpu=not args.no_gpu,
        use_timestamps=not args.no_timestamps
    )
    
    # 检查输入路径
    if os.path.isfile(args.input):
        # 单个文件
        result = transcriber.transcribe_file(args.input, args.language)
        if result["success"]:
            transcriber.save_results(result, args.output)
    elif os.path.isdir(args.input):
        # 文件夹
        transcriber.process_folder(args.input, args.output, args.language)
    else:
        print(f"错误: 路径不存在 {args.input}")

if __name__ == "__main__":
    main()

这个工具可以直接在命令行使用:

# 转录单个文件
python meeting_transcriber.py meeting.wav -o ./results

# 转录整个文件夹
python meeting_transcriber.py ./recordings/ -o ./results -l Chinese

# 不使用GPU和时间戳
python meeting_transcriber.py ./recordings/ --no-gpu --no-timestamps

它会自动处理所有音频文件,生成文本、字幕和JSON格式的结果,还有一个汇总报告,非常适合批量处理会议录音。

8. 总结

走完这一趟,你应该对怎么用Qwen3-ASR-0.6B做语音识别有了比较全面的了解。从最基础的环境搭建、模型加载,到实际的文件处理、批量操作,再到高级的时间戳和流式识别,最后还有一个完整的工具示例。

实际用下来,我感觉这个模型有几个明显的优点:一是本地运行确实让人放心,不用担心数据泄露;二是多语言支持做得不错,中英文识别都挺准的;三是功能比较全,时间戳和流式识别这些实用功能都有。

当然,它也不是完美的。比如对硬件有一定要求,如果没有独立显卡,用CPU跑会比较慢;还有长音频处理需要自己分段,不然可能内存不够。但这些通过一些技巧都能解决。

如果你刚开始接触语音识别,我建议先从简单的例子开始,跑通了再慢慢尝试更复杂的功能。遇到问题也不用急,大部分都是环境配置或者参数设置的问题,对照着错误信息调整一下就好。

语音识别的应用场景其实很多,除了会议记录,还能做视频字幕、语音笔记、客服录音分析等等。有了这个本地运行的方案,你可以在保证隐私的前提下,把这些场景都尝试一下。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐