当前位置:   article > 正文

FunASR语音识别(解决-高并发线程问题)_funasr 自动分段

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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438

三、测试

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

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

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号