3.AI语音转文本——Flask从制作到起飞,零件级颗粒度制造

写在前面的话:
作者是一名终身学习者,横跨环境、教育和IT三个行业。
IT是当前正在精进的行业,作者相信专业精神,崇尚知行合一。
作者以这个系列文章向每一个脚踏实地的web开发者致敬,希望能写出高度实用又有深度的文章帮路上的你清除障碍,欢迎你的指正和技术交流。

1. AI 工具准备

语音转文本是AI的一个子领域,本文使用百度提供的免费接口实现,专注在web server的开发上。
首先在百度AI注册相关账号,在应用列表中创建应用,这里起名为asr_test,asr是automatic speech recognition的缩写。

image

这时可以获得AppID, APIKey, Secret Key,作为后续web server功能的原材料。

2. package 管理

首先安装百度语音的相关SDK,同时更新requirements.txt,会发现baidu-aip会依赖其他的一些package,例如requests, urllib3。

$ pip install baidu-aip
$ pip freeze > requirements.txt

如果想要更好的了解package的依赖关系,可以使用pipdeptree package辅助:

$ pip install pipdeptree
$ pipdeptree
baidu-aip==2.2.10.0
  - requests [required: Any, installed: 2.21.0]
    - certifi [required: >=2017.4.17, installed: 2018.11.29]
    - chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
    - idna [required: >=2.5,<2.9, installed: 2.8]
    - urllib3 [required: >=1.21.1,<1.25, installed: 1.24.1]
Flask==1.0.2
  - click [required: >=5.1, installed: 7.0]
  - itsdangerous [required: >=0.24, installed: 1.1.0]
  - Jinja2 [required: >=2.10, installed: 2.10]
    - MarkupSafe [required: >=0.23, installed: 1.1.0]
  - Werkzeug [required: >=0.14, installed: 0.14.1]
pipdeptree==0.13.1
  - pip [required: >=6.0.0, installed: 18.1]
pytest-cov==2.6.0
  - coverage [required: >=4.4, installed: 4.5.2]
  - pytest [required: >=2.9, installed: 4.0.2]
    - atomicwrites [required: >=1.0, installed: 1.2.1]
    - attrs [required: >=17.4.0, installed: 18.2.0]
    - more-itertools [required: >=4.0.0, installed: 4.3.0]
      - six [required: >=1.0.0,<2.0.0, installed: 1.12.0]
    - pluggy [required: >=0.7, installed: 0.8.0]
    - py [required: >=1.5.0, installed: 1.7.0]
    - setuptools [required: Any, installed: 40.6.3]
    - six [required: >=1.10.0, installed: 1.12.0]
wheel==0.32.3
$ pip freeze > requirements.txt

pipdeptree可以清晰说明package的依赖关系,当前开发主要安装了两个package,一个是Flask,另一个是pytest-cov。Flask核心是两个package,Jinja2提供前端模板渲染,Werkzeug提供uWSGI服务。pytest-cov的代码覆盖率由coverage实现,test由pytest实现。

3. config 设置

然后搭建config体系,用于设置连接百度 AI 的相关参数,主要分为public和private两个部分。public部分在git版本控制中,配置应用于所有开发者的参数和无需保密的参数,例如debug和数据库的查询打印开关。private部分不在git版本控制中,配置私密参数,例如密钥和盐。

3.1 public config

在根路径下创建config.py,用来加载基本运行参数,目录结构和配置参数如下:

- FlaskTemplate
    - .circleci
        - config.yml
    - server
        - __init__.py
        - core.py
    - tests
        - unit_tests
            - __init__.py
            - test_index.py
        - __init__.py
    - venv
    - .gitignore  # 非git版本管理文件
    - config.py  # git版本管理配置参数
    - README.md  # 创建git仓库时选择生成的说明文档
    - requirements.txt  # 项目 package 安装说明
# -*- coding: utf8 -*-

DEBUG = False  # 非调试模式
SQLALCHEMY_ECHO = False  # 不输出数据库相关echo,未来连接数据库时使用

3.2 private config

在根路径下创建 instance directory,因为 Flask 默认 instance/ 下面加载 private config 文件。在 instance 路径下创建 defalut, development, production, staging 4个 python 文件,default 一般用于 local 开发,development 一般用于 dev 开发,produciton 一般用于线上生产,staging 一般用于新版本前测试。

- FlaskTemplate
    - .circleci
        - config.yml
    - instance
        - __init__.py
        - default.py  # local 配置
        - development.py  # dev 配置
        - production.py  # prod 配置
        - staging.py  # staging 配置
    - server
        - __init__.py
        - core.py
    - tests
        - unit_tests
            - __init__.py
            - test_index.py
        - __init__.py
    - venv
    - .gitignore  # 非git版本管理文件
    - config.py  # git版本管理配置参数
    - README.md  # 创建git仓库时选择生成的说明文档
    - requirements.txt  # 项目 package 安装说明
# default.py

# -*- coding: utf8 -*-

# Default values, to be used for all environments or overridden by individual environments.
# An example might be setting DEBUG = False in config/default.py and DEBUG = True in config/development.py.

DEBUG = True
SQLALCHEMY_ECHO = True

# Baidu Automatic Speech Recognition
APP_ID = "xxx"
API_KEY = "xxx"
SECRET_KEY = "xxx"
# 上面三个参数是在本文第一部分创建百度 AI 应用获取的参数

4. 项目启动更新

随着 config 的引入,项目的启动方式也需要更新,一来要添加配置和运行实例的绑定,二来要适应不同环境不同方式的启动。所以将过去的 core.py 的功能进行拆分,项目启动的代码拆分到 run.py 放在根路径中,项目配置的代码保留在 core.py 文件中,引入 config 和项目实例的绑定。

- FlaskTemplate
    - .circleci
        - config.yml
    - instance
        - __init__.py
        - default.py  # local 配置
        - development.py  # dev 配置
        - production.py  # prod 配置
        - staging.py  # staging 配置
    - server
        - __init__.py
        - core.py
    - tests
        - unit_tests
            - __init__.py
            - test_index.py
        - __init__.py
    - venv
    - .gitignore  # 非git版本管理文件
    - config.py  # git版本管理配置参数
    - README.md  # 创建git仓库时选择生成的说明文档
    - requirements.txt  # 项目 package 安装说明
    - run.py  # 项目启动文件
# core.py

# -*- coding: utf8 -*-

from flask import Flask


def create_app():
    # instance_relative_config 默认为False,设为True的时候允许 public config 文件被 instance 下的 private config 配置覆盖
    app = Flask(__name__, instance_relative_config=True)  
    # load public default configuration
    app.config.from_object('config')
    # load private default configuration
    app.config.from_pyfile('default.py')

    @app.route('/')
    def index():
        return "<h1>This is an index page.<h1/>"

    return app
# run.py

# -*- coding: utf8 -*-

from server.core import create_app

app = create_app()  # 放在这里的原因是方便后续服务器对项目的启动

# 通常作为本地开发项目启动的入口
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

启动项目检验设置情况:

Connected to pydev debugger (build 182.4505.26)
 * Serving Flask app "server.core" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
pydev debugger: process 68382 is connecting

 * Debugger is active!
 * Debugger PIN: 335-533-553

debug mode 处于开启状态,符合配置需求,虽然 public config 设置 debug 为 False,但 private config 设置 debug 为 True,配置正确。

5. instance_relative_config源码解析

Flask能够更新config配置,是由创建运行实例时的 instance_relative_config 参数控制实现的。

flask/app.py

class Flask(_PackageBoundObject):
    :param instance_relative_config: if set to ``True`` relative filenames for loading the config are assumed to be relative to the instance path instead of the application root.
    # 这个注释解释了instance_relative_config的作用是确定config加载地址,如果为 True,则加载 instance/ 下面的 private config
    
    # instance_relative_config 默认是 False,即 app 的 config 默认由 root 路径下的 public config 加载。
    def __init__(
        self,
        import_name,
        static_url_path=None,
        static_folder='static',
        static_host=None,
        host_matching=False,
        subdomain_matching=False,
        template_folder='templates',
        instance_path=None,
        instance_relative_config=False,
        root_path=None
    ):
        #: The configuration dictionary as :class:`Config`.  This behaves
        #: exactly like a regular dictionary but supports additional methods
        #: to load a config from files.
        self.config = self.make_config(instance_relative_config)
    
    def make_config(self, instance_relative=False):
        """Used to create the config attribute by the Flask constructor.
        The `instance_relative` parameter is passed in from the constructor
        of Flask (there named `instance_relative_config`) and indicates if
        the config should be relative to the instance path or the root path
        of the application.

        .. versionadded:: 0.8
        """
        root_path = self.root_path
        if instance_relative:  # 判定 config 的加载路径
            root_path = self.instance_path
        defaults = dict(self.default_config)
        defaults['ENV'] = get_env()  # 设置 ENV 参数
        defaults['DEBUG'] = get_debug_flag()
        return self.config_class(root_path, defaults)

上面是源码相关部分的简化版本,详细 app 的所有参数可以参看源码。

6. Blueprint 设置

接着开发 AI 语音转文本的具体实现,从后续功能扩展开发的角度考虑,使用 Blueprint 的配置更适合管理代码结构。先修改 core.py 下的蓝图加载

core.py 

# -*- coding: utf8 -*-

from flask import Flask


def create_app():
    app = Flask(__name__, instance_relative_config=True)
    # load public default configuration
    app.config.from_object('config')
    # load private default configuration
    app.config.from_pyfile('default.py')

    setup_blueprints(app)  # 加载蓝图

    @app.route('/')
    def index():
        return "<h1>This is an index page.<h1/>"

    return app


def setup_blueprints(app):
    from server.AI.view import blueprint as AI  # 使用 MVC 结构在 server 中为 AI 功能创建相应的 view controller

    # 蓝图参数的配置 list
    blueprints = [
        {'handler': AI, 'url_prefix': '/AI'}
    ]

    # 循环加载服务中的所有蓝图到 app 实例中
    for bp in blueprints:
        app.register_blueprint(bp['handler'], url_prefix=bp['url_prefix'])

7. AI 语音转文本

有了前面的配置铺垫,相应功能有了结构支撑,可以快速开发完成。在 server 路径下新建 AI 路径,用来处理所有 AI 的相关功能,在 AI 路径下,创建 view.py 用来处理视图功能,创建 controller.py 用来处理逻辑,暂时不引入 model 层,简化功能的实现。

- FlaskTemplate
    - .circleci
        - config.yml
    - instance
        - __init__.py
        - default.py  # local 配置
        - development.py  # dev 配置
        - production.py  # prod 配置
        - staging.py  # staging 配置
    - server
        - AI
            - __init__.py
            - controller.py
            - view.py
        - __init__.py
        - core.py
    - tests
        - unit_tests
            - __init__.py
            - test_index.py
        - __init__.py
    - venv
    - .gitignore  # 非git版本管理文件
    - config.py  # git版本管理配置参数
    - README.md  # 创建git仓库时选择生成的说明文档
    - requirements.txt  # 项目 package 安装说明
    - run.py  # 项目启动文件
# -*- coding: utf8 -*-

from flask import Blueprint

from server.AI import controller

blueprint = Blueprint('AI', __name__)  # 生成 AI 的蓝图实例,在 core.py 会 import 并在 app 中 register


@blueprint.route('/asr')
def asr():
    rsp = controller.asr().pop()  # 调用 controller 中的处理逻辑,完成相关功能

    return rsp
# -*- coding: utf8 -*-

from flask import current_app  # 引入当前实例

from aip import AipSpeech  # 引入百度 AI 的工具

from uploads import uploads_path

# 读取本地音频文件内容
def read_file(path):
    """
    read file content
    :param path: 
    :return: 
    """
    with open(path, 'rb') as f:
        return f.read()


def asr():
    """
    automatic speech recognition
    :return: 
    """
    # Baidu Cloud AI, get config params
    app_id = current_app.config['APP_ID']
    api_key = current_app.config['API_KEY']
    secret_key = current_app.config['SECRET_KEY']

    # 生成百度 AI 实例连接的 client
    client = AipSpeech(app_id, api_key, secret_key)
    # 
    def asr():
    """
    automatic speech recognition
    :return: 
    """
    # Baidu Cloud AI
    app_id = current_app.config['APP_ID']
    api_key = current_app.config['API_KEY']
    secret_key = current_app.config['SECRET_KEY']

    # 创建连接百度 AI 的 client
    client = AipSpeech(app_id, api_key, secret_key)
    # 连接 AI 模型返回语音转文本的结果
    rsp = client.asr(read_file(f'{uploads_path}/stock.wav'), 'wav', 16000, {'dev_pid': 1536})

    return rsp['result']

这一期的开发不涉及前端录音的功能,先实现本地录音的语音转文本功能,本地语音文件放在根目录下的 uploads 下,设置如下:

- FlaskTemplate
    - .circleci
        - config.yml
    - instance
        - __init__.py
        - default.py  # local 配置
        - development.py  # dev 配置
        - production.py  # prod 配置
        - staging.py  # staging 配置
    - server
        - AI
            - __init__.py
            - controller.py
            - view.py
        - __init__.py
        - core.py
    - tests
        - unit_tests
            - __init__.py
            - test_index.py
        - __init__.py
    - uploads
        - __init__.py  # 存放当前路径的变量
        - stock.wav  # 语音转文本本地 demo 文件
    - venv
    - .gitignore  # 非git版本管理文件
    - config.py  # git版本管理配置参数
    - README.md  # 创建git仓库时选择生成的说明文档
    - requirements.txt  # 项目 package 安装说明
    - run.py  # 项目启动文件

因为文件使用需要相应路径,所以在 uploads/init.py 中写入了获取当前路径的变量

# -*- coding: utf8 -*-

import os

# 获取当前路径
uploads_path = os.path.abspath(os.path.dirname(__file__))

8. 实现

通过上面的开发,实现了对项目中根路径下 uploads/stock.wav 的自动语音转文本的功能。


image

相关链接
2.CI(Continuous Integration)——Flask从制作到起飞,零件级颗粒度制造
1.初始化——Flask从制作到起飞,零件级颗粒度制造

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容