FunASR语音识别(解决-高并发线程问题)

一、FunASR

官网

在我的另一个博客有介绍FunASR,并且进行了语者分离,不过最近FunASR自带了语者分离,挺好挺好,但是一直看社区就是大家都用python写,会出现线程不安全问题,群里有大佬说使用多台服务器,然后用nginx做代理,这不是妥妥土豪行为吗,感觉很浪费

vad出现的问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方案解决:
图上部分是大佬给的解决方案
图下部分是我给的解决方案方案

在这里插入图片描述

二、我的方案:上代码(队列解决线程并发问题)

import os

import uuid

import copy

import json

import logging

import queue

import threading



import numpy as np

import torch

from flask import Flask

from flask import request, jsonify

from modelscope.pipelines import pipeline

from modelscope.utils.constant import Tasks



app = Flask(__name__)

# 创建一个队列

pipeline_queue = queue.Queue()

# 实例对象的计数器来存储实例的数量

created_instances = 0

# logging.basicConfig(filename='app.log', level=logging.INFO)

# logger = logging.getLogger('info')

# # 再创建一个handler,用于输出到控制台

# ch = logging.StreamHandler()

# ch.setLevel(logging.INFO)

# # 定义handler的输出格式

# formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# ch.setFormatter(formatter)

# # 给logger添加handler

# logger.addHandler(ch)





def flask_content_type(req) -> dict:

    """根据不同的content_type来解析数据"""

    if req.method == 'POST' or req.method == "PUT":

        if 'application/x-www-form-urlencoded' == req.content_type or 'form-data' in req.content_type:

            data = req.form

        else:  # 无法被解析出来的数据

            if req.data:

                data = json.loads(req.data)

            else:

                raise Exception('无法解析')

    elif req.method == 'GET':

        data = req.args

    else:

        raise Exception('不支持的请求方式')

    return copy.copy(data)





def create_pipelines(num_pipelines):

    """

    @Description:num_pipelines:创建pipeline实例的数量,根据你的显存大小创建,一个实例需要占用2GB

    """

    for _ in range(num_pipelines):

        global created_instances

        inference_pipeline = pipeline(

            task=Tasks.auto_speech_recognition,

            model='/root/autodl-tmp/models_from_modelscope/damo/speech_paraformer-large-vad-punc-spk_asr_nat-zh-cn',

            model_revision='v0.0.2',

            vad_model='/root/autodl-tmp/models_from_modelscope/damo/speech_fsmn_vad_zh-cn-16k-common-pytorch',

            punc_model='/root/autodl-tmp/models_from_modelscope/damo/punc_ct-transformer_cn-en-common-vocab471067-large')

        pipeline_queue.put(inference_pipeline)

        print("=========成功创建实例=========")

        # 更新已创建实例的数量

        created_instances += 1

        print(f"=====队列现有空闲实例数量为{pipeline_queue.qsize()}========")

        print(f"=====实例总数量数量为{created_instances}========")

        print("============================")





def create_pipelines_thread(num_pipelines):

    # 创建线程

    thread = threading.Thread(target=create_pipelines, args=(num_pipelines,))

    # 启动线程

    thread.start()

    return thread





def default_dump(obj):

    """Convert numpy classes to JSON serializable objects."""

    if isinstance(obj, (np.integer, np.floating, np.bool_)):

        return obj.item()

    elif isinstance(obj, np.ndarray):

        return obj.tolist()

    else:

        return obj





@app.route('/queue_size', methods=['GET'])

def get_queue_size():

    # 获取队列数量

    queue_size = pipeline_queue.qsize()



    # 构建响应

    response = {

        'queue_size': queue_size

    }



    # 返回响应

    return jsonify(response), 200





@app.route('/created_instances', methods=['GET'])

def get_created_instances():

    # 获取已创建的实例数量

    response = {

        'created_instances': created_instances

    }

    # 返回响应

    return jsonify(response), 200





@app.route('/add_pipeline/<int:num>', methods=['GET'])

def add_pipeline_queue(num):

    global created_instances

    if (created_instances >= 10):

        return jsonify({'error': f"现有实例数量为{created_instances},无法再添加"})



    print("=========开始创建实例=========")

    print(f"=====队列现有空闲实例数量为{pipeline_queue.qsize()}========")

    thread = create_pipelines_thread(num)

    # 等待线程结束

    thread.join()

    print("=========实例创建结束=========")

    print(pipeline_queue.qsize())



    return jsonify({'success': f"队列现有空闲实例数量为{pipeline_queue.qsize()}!现有实例数量为{created_instances}"})





@app.route('/', methods=['POST'])

def result_test():

    dates = flask_content_type(request).copy()

    print(dates)

    return jsonify({'success': dates})





@app.route('/transcribe', methods=['POST'])

def transcribe():

    print("======队列剩余======")

    print(pipeline_queue.qsize())

    # 第一步,获取请求体



    if 'audio_file' in request.files:

        audio_file = request.files['audio_file']

        file_ext = os.path.splitext(audio_file.filename)[1]

        if file_ext.lower() not in ['.wav', '.mp3']:

            return jsonify({'error': str('Error: Audio file must be either .wav or .mp3')}), 500

        else:

            try:

                # 将音频文件保存到临时文件夹中

                temp_dir_path = 'temp'

                if not os.path.exists(temp_dir_path):

                    os.makedirs(temp_dir_path)

                # 保存上传的临时文件

                file_extension = os.path.splitext(audio_file.filename)[1]

                unique_filename = str(uuid.uuid4()) + file_extension

                temp_file_path = os.path.join(temp_dir_path, unique_filename)

                audio_file.save(temp_file_path)

                return start_asr(temp_file_path)



            except Exception as e:

                return jsonify({'error': str(e)}), 500

            finally:

                # 删除临时文件

                os.remove(temp_file_path)



    else:

        dates = flask_content_type(request).copy()

        return start_asr(dates['file_url'])





def start_asr(temp_file_path):

    import time



    # 记录开始时间

    start_time = time.time()

    inference_pipeline = pipeline_queue.get()

    # 使用 inference pipeline 进行语音转写

    asr_result = inference_pipeline(audio_in=temp_file_path, batch_size_token=5000,

                                    batch_size_token_threshold_s=40,

                                    max_single_segment_time=6000)

    try:

        transform = time.time() - start_time



        asr_result["transform_time"] = transform



        # 将语音识别结果转换为json格式

        result = json.dumps(asr_result, ensure_ascii=False, default=default_dump)

        return result

    except Exception as e:

        print(str(e))

        # 返回错误信息

        return jsonify({'error': str(e)}), 500

    finally:

        pipeline_queue.put(inference_pipeline)





def start_flask_app(port):

    """

    启动 Flask 应用程序并运行在指定端口上

    在调用 Flask 的 app.run 方法后,应用会进入监听状态,等待客户端发起请求。这意味着应用会一直停留在 app.run() 这一行,不会继续执行后续的 Python 代码。

    启动 Flask 应用程序前,使用多线程的方式创建:我这里是32G显存所以默认创建10个实例

    """

    print("============================")

    print("======开始异步创建实例=========")

    print("============================")

    try:

        # 清除显卡

        torch.cuda.empty_cache()



    except Exception as e:

        # 返回错误信息

        print(e)

    create_pipelines_thread(5)

    app.run(port=port)





if __name__ == '__main__':

    start_flask_app(9501)

三、测试

二已经给你完整的示例了,所以我就不测了,我都上生产了,你们自己用postman或者代码试一下把!有问题再联系我18956043585(微信同号)
兄弟们,这是我的解决方案,欢迎交流,如果有帮助,或者有问题,欢迎回来留言,分享一下你的兴奋

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四,最新版本的funasr好像不兼容上面生成实例的模型

如果你是

import os
import traceback

import uuid

import copy

import json

import logging

import queue

import threading

import numpy as np

import torch

from flask import Flask

from flask import request, jsonify

from modelscope.pipelines import pipeline

from modelscope.utils.constant import Tasks

app = Flask(__name__)

# 创建一个队列

pipeline_queue = queue.Queue()

# 实例对象的计数器来存储实例的数量

created_instances = 0


# logging.basicConfig(filename='app.log', level=logging.INFO)

# logger = logging.getLogger('info')

# # 再创建一个handler,用于输出到控制台

# ch = logging.StreamHandler()

# ch.setLevel(logging.INFO)

# # 定义handler的输出格式

# formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# ch.setFormatter(formatter)

# # 给logger添加handler

# logger.addHandler(ch)


def flask_content_type(req) -> dict:
    """根据不同的content_type来解析数据"""

    if req.method == 'POST' or req.method == "PUT":

        if 'application/x-www-form-urlencoded' == req.content_type or 'form-data' in req.content_type:

            data = req.form

        else:  # 无法被解析出来的数据

            if req.data:

                data = json.loads(req.data)

            else:

                raise Exception('无法解析')

    elif req.method == 'GET':

        data = req.args

    else:

        raise Exception('不支持的请求方式')

    return copy.copy(data)


def create_pipelines(num_pipelines):
    """

    @Description:num_pipelines:创建pipeline实例的数量,根据你的显存大小创建,一个实例需要占用2GB

    """

    for _ in range(num_pipelines):
        inference_pipeline = pipeline(
            task=Tasks.auto_speech_recognition,
            model='iic/speech_paraformer-large-vad-punc-spk_asr_nat-zh-cn',
            model_revision='v2.0.4',
            vad_model='iic/speech_fsmn_vad_zh-cn-16k-common-pytorch', vad_model_revision="v2.0.4",
            punc_model='iic/punc_ct-transformer_cn-en-common-vocab471067-large', punc_model_revision="v2.0.4"
        )
        pipeline_queue.put(inference_pipeline)

    print("=========成功创建实例=========")

    # 更新已创建实例的数量

    created_instances += 1

    print(f"=====队列现有空闲实例数量为{pipeline_queue.qsize()}========")

    print(f"=====实例总数量数量为{created_instances}========")

    print("============================")


def create_pipelines_thread(num_pipelines):
    # 创建线程

    thread = threading.Thread(target=create_pipelines, args=(num_pipelines,))

    # 启动线程

    thread.start()

    return thread


def default_dump(obj):
    """Convert numpy classes to JSON serializable objects."""

    if isinstance(obj, (np.integer, np.floating, np.bool_)):

        return obj.item()

    elif isinstance(obj, np.ndarray):

        return obj.tolist()

    else:

        return obj


@app.route('/queue_size', methods=['GET'])
def get_queue_size():
    # 获取队列数量

    queue_size = pipeline_queue.qsize()

    # 构建响应

    response = {

        'queue_size': queue_size

    }

    # 返回响应

    return jsonify(response), 200


@app.route('/created_instances', methods=['GET'])
def get_created_instances():
    # 获取已创建的实例数量

    response = {

        'created_instances': created_instances

    }

    # 返回响应

    return jsonify(response), 200


@app.route('/add_pipeline/<int:num>', methods=['GET'])
def add_pipeline_queue(num):
    global created_instances

    if (created_instances >= 10):
        return jsonify({'error': f"现有实例数量为{created_instances},无法再添加"})

    print("=========开始创建实例=========")

    print(f"=====队列现有空闲实例数量为{pipeline_queue.qsize()}========")

    thread = create_pipelines_thread(num)

    # 等待线程结束

    thread.join()

    print("=========实例创建结束=========")

    print(pipeline_queue.qsize())

    return jsonify({'success': f"队列现有空闲实例数量为{pipeline_queue.qsize()}!现有实例数量为{created_instances}"})


@app.route('/', methods=['POST'])
def result_test():
    dates = flask_content_type(request).copy()

    print(dates)

    return jsonify({'success': dates})


@app.route('/transcribe', methods=['POST'])
def transcribe():
    print("======队列剩余======")

    print(pipeline_queue.qsize())

    # 第一步,获取请求体

    if 'audio_file' in request.files:

        audio_file = request.files['audio_file']

        file_ext = os.path.splitext(audio_file.filename)[1]

        if file_ext.lower() not in ['.wav', '.mp3']:

            return jsonify({'error': str('Error: Audio file must be either .wav or .mp3')}), 500

        else:

            try:

                # 将音频文件保存到临时文件夹中

                temp_dir_path = 'temp'

                if not os.path.exists(temp_dir_path):
                    os.makedirs(temp_dir_path)

                # 保存上传的临时文件

                file_extension = os.path.splitext(audio_file.filename)[1]

                unique_filename = str(uuid.uuid4()) + file_extension

                temp_file_path = os.path.join(temp_dir_path, unique_filename)

                audio_file.save(temp_file_path)

                return start_asr(temp_file_path)



            except Exception as e:

                return jsonify({'error': str(e)}), 500

            finally:

                # 删除临时文件

                os.remove(temp_file_path)



    else:

        dates = flask_content_type(request).copy()

        return start_asr(dates['file_url'])


def start_asr(temp_file_path):
    import time

    # 记录开始时间

    start_time = time.time()

    inference_pipeline = pipeline_queue.get()

    # 使用 inference pipeline 进行语音转写

    asr_result = inference_pipeline(input=temp_file_path, batch_size_s=300, hotword='魔搭')

    try:

        transform = time.time() - start_time

        asr_result[0]["transform_time"] = transform

        # 将语音识别结果转换为json格式

        result = json.dumps(asr_result, ensure_ascii=False, default=default_dump)

        return result

    except Exception as e:

        traceback.print_exc()
        print(str(e))
        # 返回错误信息

        return jsonify({'error': str(e)}), 500

    finally:

        pipeline_queue.put(inference_pipeline)


def start_flask_app(port):
    """

    启动 Flask 应用程序并运行在指定端口上

    在调用 Flask 的 app.run 方法后,应用会进入监听状态,等待客户端发起请求。这意味着应用会一直停留在 app.run() 这一行,不会继续执行后续的 Python 代码。

    启动 Flask 应用程序前,使用多线程的方式创建:我这里是32G显存所以默认创建10个实例

    """

    print("============================")

    print("======开始异步创建实例=========")

    print("============================")

    try:

        # 清除显卡

        torch.cuda.empty_cache()



    except Exception as e:

        # 返回错误信息
        traceback.print_exc()
        print(e)

    create_pipelines_thread(5)

    app.run(host='0.0.0.0', port=port)


if __name__ == '__main__':
    start_flask_app(9501)

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 多线程技术可以通过锁机制来处理数据高并发问题,即在多个线程同时访问共享数据时,通过加锁来保证同一时间只有一个线程能够访问该数据,从而避免数据的冲突和错误。此外,还可以采用线程池、队列等技术来优化多线程的性能和效率。 ### 回答2: 多线程技术是一种处理数据高并发问题的有效方式。通过多线程,可以同时处理多个任务,提高系统的并发处理能力。 首先,多线程技术可以将一个任务分解为多个小任务,每个线程负责处理其中的一部分。这样,多个线程可以同时执行,加快任务的处理速度。例如,在一个电商网站中,当用户同时发起多个订单请求时,可以使用多线程技术将每个订单请求分配给不同的线程进行处理,从而提高订单处理的效率。 其次,多线程技术可以减少等待时间,提升系统的响应速度。在高并发的情况下,如果只有单线程处理任务,会导致大量的任务排队等待,造成用户长时间等待的情况。而多线程技术可以同时处理多个任务,减少任务的等待时间,提升系统的响应速度。例如,在一个社交媒体平台中,用户同时发送多个消息时,可以使用多线程技术将每个消息分配给不同的线程进行处理,从而减少用户等待的时间,提高用户体验。 此外,多线程技术还可以提高系统的资源利用率。在处理数据高并发的情况下,如果只使用单线程处理任务,会导致大量的CPU和内存资源闲置。而多线程技术可以同时利用多个处理器核心,充分发挥系统资源的作用,提高资源利用率。例如,在一个视频编码的应用中,可以使用多线程技术将不同的视频帧分配给不同的线程进行编码,从而充分利用多个处理器核心,提高视频编码的效率。 总之,多线程技术是一种处理数据高并发问题的有效手段,可以提高系统的并发处理能力、减少等待时间、提升系统响应速度以及提高资源利用率。通过合理地使用多线程技术,可以更好地满足用户的需求,提升系统的性能。 ### 回答3: 多线程技术可以通过以下几种方式来处理数据高并发问题。 首先,可以将数据划分为多个部分,并为每个部分创建一个线程进行处理。通过这种方式,每个线程只需要处理一个部分的数据,可以并行地进行处理,提高程序的处理能力。 其次,多线程技术可以通过任务队列的方式来处理高并发的数据。当有大量的数据需要处理时,可以将数据放入一个任务队列中,然后创建多个线程从队列中获取任务进行处理。通过任务队列,可以有效地将任务分发给不同的线程,避免多个线程同时处理同一份数据,提高程序的并发处理能力。 另外,多线程技术可以通过控制并发访问共享资源的方式来处理数据高并发问题。当多个线程需要访问同一个共享资源时,可以使用锁机制来控制只有一个线程可以访问该资源,其他线程需要等待。通过这种方式可以避免多个线程同时修改同一份数据导致的并发冲突,保证数据的一致性。 此外,多线程技术还可以使用线程池来处理数据的高并发线程池可以预先创建一定数量的线程,在需要处理数据时从线程池中获取线程进行处理,处理完之后将线程释放回线程池。通过线程池,可以避免频繁地创建和销毁线程,提高程序的性能。 综上所述,多线程技术可以通过划分数据、使用任务队列、控制资源访问和使用线程池等方式来处理数据的高并发问题,提高程序的并发处理能力和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码浪人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值