发布于: Oct 14, 2022

语音云服务器为语音的转录提供了更为高效,便利的平台,那么具体我们应该如何操作呢?本文将会为您做详细的介绍。

我们的第一步,是查看在缺少自定义词汇表或其他修改时,Amazon Transcribe 的基本性能,并以此为基础建立基准准确率指标。

使用 transcribe 函数即可启动转录作业。我们稍后可以使用 vocab_name 参数以指定自定义词汇表,但当前默认为 None。具体参见以下代码:

transcribe(job_names[0], folder_path+all_videos[0], BUCKET)

请稍等一会儿,直到转录作业显示为 COMPLETED。一段 1015 分钟长度的视频,转录流程通常最多需要 5 分钟。

在转录作业完成之后,结果将被存储在指定 BUCKET 中名为YOUR_JOB_NAME.json 的输出 JSON 文件内。使用 get_transcript_text_and_timestamps 函数即可解析此项输出,并返回多个有用的数据结构。在调用之后,all_sentences_and_times 将为每段转录视频生成一个对象列表,其中包含带有开始时间、结束时间与置信度得分的句子。要将这些句子保存为文本文件以备后续使用,请输入以下代码:

file0 = open("originaltranscript.txt","w")

    for tup in sentences_and_times_1:

        file0.write(tup['sentence'] + "\n")

file0.close()

要查看置信度得分分布,请输入以下代码:

from matplotlib import pyplot as plt

plt.style.use('ggplot')

flat_scores_list = all_scores[0]

plt.xlim([min(flat_scores_list)-0.1, max(flat_scores_list)+0.1])

plt.hist(flat_scores_list, bins=20, alpha=0.5)

plt.title('Plot of confidence scores')

plt.xlabel('Confidence score')

plt.ylabel('Frequency')

plt.show()

以下图所示,为置信度得分的分布情况。

接下来,我们过滤掉高置信度得分,专注研究置信度得分较低的部分。

您可以尝试不同的阈值,查看有多少个单词低于该阈值。在本用例当中,我们将阈值设定为 0.4,低于此阈值的单词共有 16 个。任何低于此阈值的单词序列,都将被发送给工作团队以进行人工审核。

您可以尝试不同的阈值并观察其在 Amazon A2I 工作流中所对应的任务数量,据此在捕捉到的转录错误与希望投入的审核时间/资源之间找到平衡点。换句话说,使用较高的阈值虽然能够捕捉到更多疑似转录错误,但也会快速增加误报数量——换言之,低置信度转录结果并不一定包含重要的技术术语转录错误。好在大家可以在工作流中快速尝试不同阈值设定,并将结果交由工作人员进行人工审核。具体参见以下代码:

THRESHOLD = 0.4

# Filter scores that are less than THRESHOLD

all_bad_scores = [i for i in flat_scores_list if i < THRESHOLD]print(f"There are {len(all_bad_scores)} words that have confidence score less than {THRESHOLD}")

plt.xlim([min(all_bad_scores)-0.1, max(all_bad_scores)+0.1])

plt.hist(all_bad_scores, bins=20, alpha=0.5)

plt.title(f'Plot of confidence scores less than {THRESHOLD}')

plt.xlabel('Confidence score')

plt.ylabel('Frequency')

plt.show()

您将获得以下输出结果:

There are 16 words that have confidence score less than 0.4

下图所示,为置信度得分低于 0.4 的分布情况。

在尝试使用不同阈值时,大家会发现很多置信度较低的单词。一般来说,针对高新技术领域的术语往往很难一次性转录正确,因此最重要的就是确切捕捉到这些术语,并将其合并至自定义词汇表当中。

我们的下一步工作是创建一套人工审核工作流(或者叫流定义),负责将低置信度得分发送给人工审核员,并对他们校正后的转录结果进行检索。随附的 Jupyter notebook 中包含以下步骤说明:

  1. 创建一支由工作人员组成的工作团队,对预测结果进行审核。在本用例中,您可以通过内部工作团队引导 Amazon A2I 将人工审核任务发送给自己,借此预览工作人员 UI
  2. 创建一套工作任务模板,用于将任务交付给各个工作人员。此模板将使用您提供的输入数据对工作人员做出指导,同时通过交互式工具引导他们一步步完成审核任务。
  3. 创建一套人工审核工作流,也可称为流定义。大家可以使用流定义配置您的人力资源以及人工审核的具体任务分配方式。
  4. 建立人工循环以启动人工审核工作流,根据需求发送数据以供工作人员审核。在本用例中,我们使用自定义任务类型并通过 Amazon A2I Runtime API 启动人工循环任务。只要对 StartHumanLoop 进行调用,即可将任务发送给人工审核员。

在笔记本中,您可以使用 Amazon Python SDK (Boto3)  create_flow_definition 函数创建一套人工审核工作流,也可以 Amazon SageMaker 控制台上创建人工审核工作流

设置工作人员任务 UI

Amazon A2I 使用 Liquid(一种开源模板语言)以动态方式将数据插入至 HTML 文件内。

在本用例中,我们希望每项任务都能向人工审核员展示视频中出现低置信度单词的对应部分,并播放与之对应的语音。这套 HTML 模板包含三个主要部分:

  • 带有重播按钮的播放器,仅允许审核员播放特定视频段落。
  • 供审核员输入及提交所听到内容的表单。
  • 使用 JavaScript 编写逻辑,为重播按钮提供重播功能。

以下代码为本示例中使用的模板:

 

<head>
    <style>
        h1 {
            color: black;
            font-family: verdana;
            font-size: 150%;
        }
    </style></head><script src="https://assets.crowd.aws/crowd-html-elements.js"></script>
<crowd-form>
    <video id="this_vid">
        <source src="{{ task.input.filePath | grant_read_access }}"
            type="audio/mp4">
        Your browser does not support the audio element.
    </video>
    <br />
    <br />
    <crowd-button onclick="onClick(); return false;"><h1> Click to play video section!</h1></crowd-button> 

    <h3>Instructions</h3>
    <p>Transcribe the audio clip </p>
    <p>Ignore "umms", "hmms", "uhs" and other non-textual phrases. </p>
    <p>The original transcript is <strong>"{{ task.input.original_words }}"</strong>. If the text matches the audio, you can copy and paste the same transcription.</p>
    <p>Ignore "umms", "hmms", "uhs" and other non-textual phrases.
    If a word is cut off in the beginning or end of the video clip, you do NOT need to transcribe that word.
    You also do NOT need to transcribe punctuation at the end of clauses or sentences.
    However, apostrophes and punctuation used in technical terms should still be included, such as "Denny's" or "file_name.txt"</p>
    <p><strong>Important:</strong> If you encounter a technical term that has multiple words,
    please <strong>hyphenate</strong> those words together. For example, "k nearest neighbors" should be transcribed as "k-nearest-neighbors."</p>
    <p>Click the space below to start typing.</p>
    <full-instructions header="Transcription Instructions">
        <h2>Instructions</h2>
        <p>Click the play button and listen carefully to the audio clip. Type what you hear in the box
            below. Replay the clip by clicking the button again, as many times as needed.</p>
    </full-instructions>
</crowd-form>
<script>
    var video = document.getElementById('this_vid');
    video.onloadedmetadata = function() {
        video.currentTime = {{ task.input.start_time }};
    };
    function onClick() {
        video.pause();
        video.currentTime = {{ task.input.start_time }};
        video.play();
        video.ontimeupdate = function () {
            if (video.currentTime >= {{ task.input.end_time }}) {
                video.pause()
            }
        }
    }</script>

{{ task.input.filePath | grant_read_access }}字段允许您使用S3存储桶内的视频位置路径授予访问权限,并将视频内容显示给审核人员。为了防止审核员观看视频中的不相关部分,我们从视频标签中省略了controls参数,并引入一个重播按钮以控制能够重播的具体范围。

在视频播放器下,<crowd-text-area> HTML标签创建一份提交表单,以供审订员进行键入与提交操作。

HTML片段的末尾,script 标签括起的部分包含用于实现重播按钮功能的JavaScript逻辑。 {{ task.input.start_time }} {{task.input.end_time }} 字段则允许您为当前任务注入待转录视频内对应部分的开始与结束时间。

您可以使用Amazon Python SDK (Boto3)  create_human_task_ui函数创建一套工作人员任务模板,也可以 Amazon SageMaker控制台上创建人工任务模板

创建人工循环

在流定义设置完成之后,我们即可使用 Amazon Transcribe 并启动人工循环。在遍历转录词汇列表及置信度得分的过程中,一旦置信度得分低于某一阈值 CONFIDENCE_SCORE_THRESHOLD,则据此创建对应的人工循环了。人工循环属于一项人工审核任务,允许工作人员审核 Amazon Transcribe 难以正确转录的视频片段。

在这方面,最重要的在于如何处理低置信度单词,特别是归属于转录错误短语的技术术语。为了解决这类情况,您可以使用专门的函数以给定索引为中心获取单词序列,以及该序列的开始与结束时间戳。具体参见以下代码:

def get_word_neighbors(words, index):
    """
    gets the words transcribe found at most 3 away from the input index
    Returns:
        list: words at most 3 away from the input index
        int: starting time of the first word in the list
        int: ending time of the last word in the list
    """
    i = max(0, index - 3)
    j = min(len(words) - 1, index + 3)
    return words[i: j + 1], words[i]["start_time"], words[j]["end_time"]

对于我们遇到的每一个低置信度得分单词,都需要将与之关联的相邻单词序列发送给人工审核员,具体参见以下代码:

human_loops_started = []
CONFIDENCE_SCORE_THRESHOLD = THRESHOLD
i = 0for obj in confidences_1:
    word = obj["content"]
    neighbors, start_time, end_time = get_word_neighbors(confidences_1, i)
    
    # Our condition for when we want to engage a human for review
    if (obj["confidence"] < CONFIDENCE_SCORE_THRESHOLD):
        
        # get the original sequence of words
        sequence = ""
        for block in neighbors:
            sequence += block['content'] + " "
        
        humanLoopName = str(uuid.uuid4())
        # "initialValue": word,
        inputContent = {
            "filePath": job_uri_s3,
            "start_time": start_time,
            "end_time": end_time,
            "original_words": sequence
        }
        start_loop_response = a2i.start_human_loop(
            HumanLoopName=humanLoopName,
            FlowDefinitionArn=flowDefinitionArn,
            HumanLoopInput={
                "InputContent": json.dumps(inputContent)
            }
        )
        human_loops_started.append(humanLoopName)
        # print(f'Confidence score of {obj["confidence"]} is less than the threshold of {CONFIDENCE_SCORE_THRESHOLD}')
        # print(f'Starting human loop with name: {humanLoopName}')
        # print(f'Sending words from times {start_time} to {end_time} to review')
        print(f'The original transcription is ""{sequence}"" \n')

    i=i+1

在第一段视频中,大家应该会看到如下代码输出:

========= Fully-Managed Notebook Instances with Amazon SageMaker - a Deep Dive.mp4 =========
The original transcription is "show up Under are easy to console "

The original transcription is "And more cores see is compute optimized "

The original transcription is "every version of Annecy two instance is "

The original transcription is "distributing data sets wanted by putt mode "

The original transcription is "onto your EBS volumes And again that's "

The original transcription is "of those example No books are open "

The original transcription is "the two main ones markdown is gonna "

The original transcription is "I started using Boto three but I "

The original transcription is "absolutely upgrade on bits fun because you "

The original transcription is "That's the python Asi que We're getting "

The original transcription is "the Internet s Oh this is from "

The original transcription is "this is from Sarraf He's the author "

The original transcription is "right up here then the title of "

The original transcription is "but definitely use Lambda to turn your "

The original transcription is "then edit your ec2 instance or the "

Number of tasks sent to review: 15

在任务完成之后,我们将看到错误的转录结果以及相应的视频片段,具体参见以下截屏。

其中的人工循环状态已经显示为Completed。事实上,不必等待全部人工审核任务逐一完成,我们就已经可以开始下一步。一般来讲,只需要完成 35 项审核,就足以了解应如何从结果中提取技术术语,详见以下代码:

completed_human_loops = []for human_loop_name in human_loops_started:
    resp = a2i.describe_human_loop(HumanLoopName=human_loop_name)
    print(f'HumanLoop Name: {human_loop_name}')
    print(f'HumanLoop Status: {resp["HumanLoopStatus"]}')
    print(f'HumanLoop Output Destination: {resp["HumanLoopOutput"]}')
    print('\n')
    
    if resp["HumanLoopStatus"] == "Completed":
        completed_human_loops.append(resp)

在完成所有任务之后,Amazon A2I 会将结果存储在您的 S3 存储桶内,并发送至 Amazon CloudWatch Event(您可以在 Amazon Web Services 管理控制台上检查这些事件)。您可以通过 S3 存储桶OUTPUT_PATH 查看结果,并使用以下代码进行结果输出:

import reimport pprint

pp = pprint.PrettyPrinter(indent=4)
for resp in completed_human_loops:
    splitted_string = re.split('s3://' +  BUCKET + '/', resp['HumanLoopOutput']['OutputS3Uri'])
    output_bucket_key = splitted_string[1]

    response = s3.get_object(Bucket=BUCKET, Key=output_bucket_key)
    content = response["Body"].read()
    json_output = json.loads(content)
    pp.pprint(json_output)
    print('\n')

您可以使用由人工审核员提供的转录更正内容以解析结果,借此识别出需要添加到自定义词汇表内的特定领域术语。要从全部人工审核员处获取完整的更正单词列表,请输入以下代码:

corrected_words = []
for resp in completed_human_loops:
    splitted_string = re.split('s3://' +  BUCKET + '/', resp['HumanLoopOutput']['OutputS3Uri'])
    output_bucket_key = splitted_string[1]

    response = s3.get_object(Bucket=BUCKET, Key=output_bucket_key)
    content = response["Body"].read()
    json_output = json.loads(content)
    
    # add the human-reviewed answers split by spaces
    corrected_words += json_output['humanAnswers'][0]['answerContent']['transcription'].split(" ")

我们希望解析这些单词,找到相对不常见的词汇。一种比较简单的方法是使用大型英语语料库,并验证该语料库中是否存在经过人工审核的单词。在本用例中,我们使用的是来自自然语言工具包NLTK)的英语语料库——这是一套开源的社区驱动库,主要用于自然语言处理研究。具体请参见以下代码:

# Create dictionary of English words
# Note that this corpus of words is not 100% exhaustive
import nltk
nltk.download(‘words’)
from nltk.corpus import words
my_dict=set(words.words())
word_set = set([])
for word in remove_contractions(corrected_words):
if word:
if word.lower() not in my_dict:
if word.endswith(‘s’) and word[:-1] in my_dict:
print(“”)
elif word.endswith(“‘s”) and word[:-2] in my_dict:
print(“”)
else:
word_set.add(word)
for word in word_set:
print(word)

您所获得的实际词汇可能有所区别,具体取决于您使用的视频与设定的阈值。以下代码为播放列表中第一与第三段视频在 Amazon A2I 中的输出结果示例(请参阅前文中的「由此开始」部分):

including
machine-learning
grabbing
amazon
boto3
started
t3
called
sarab
ecr
using
ebs
internet
jupyter
distributing
opt/ml
optimized
desktop
tokenizing
s3
sdk
encrypted
relying
sagemaker
datasets
upload
iam
gonna
managing
wanna
vpc
managed
mars.r
ec2
blazingtext

有了这些技术术语,您现在可以更轻松地手动创建自定义词汇表,帮助 Amazon Transcribe 更准确地识别出技术术语。您可以使用自定义词汇表告知 Amazon Transcribe 各项技术术语的发音方式与显示方式。关于自定义词汇表的更多详细信息,请参阅使用表创建一份自定义词汇表

当您处理同一主题下的其他视频时,可以不断更新这份词汇表。在逐步学习之后,系统在转录新视频时需要添加的全新技术术语也将逐渐减少。

通过第一与第三段视频,我们在将THRESHOLD置信值设定为 0.5 的前提下对 Amazon A2I 结果进行解析并整理出一份词汇表。大家可以在笔记本的其余部分中使用这份词汇表。

finalized_words=[['Phrase','IPA','SoundsLike','DisplayAs'], # This top line denotes the column headers of the text file.
                 ['machine-learning','','','machine learning'],
                 ['amazon','','am-uh-zon','Amazon'],
                 ['boto-three','','boe-toe-three','Boto3'],
                 ['T.-three','','tee-three','T3'],
                 ['Sarab','','suh-rob','Sarab'],
                 ['E.C.R.','','ee-see-are','ECR'],
                 ['E.B.S.','','ee-bee-ess','EBS'],
                 ['jupyter','','joo-pih-ter','Jupyter'],
                 ['opt-M.L.','','opt-em-ell','/opt/ml'],
                 ['desktop','','desk-top','desktop'],
                 ['S.-Three','','ess-three','S3'],
                 ['S.D.K.','','ess-dee-kay','SDK'],
                 ['sagemaker','','sage-may-ker','SageMaker'],
                 ['mars-dot-r','','mars-dot-are','mars.R'],
                 ['I.A.M.','','eye-ay-em','IAM'],
                 ['V.P.C.','','','VPC'],
                 ['E.C.-Two','','ee-see-too','EC2'],
                 ['blazing-text','','','BlazingText'],
                ]

在将自定义词汇表保存为文本文件并将其上传至 S3 存储桶之后,大家即可为其命名以创建自定义词汇表,以便 Amazon Transcribe 进行直接调用:

# The name of your custom vocabulary must be unique!
vocab_improved='sagemaker-custom-vocab'

transcribe = boto3.client("transcribe")
response = transcribe.create_vocabulary(
    VocabularyName=vocab_improved,
    LanguageCode='en-US',
    VocabularyFileUri='s3://' + BUCKET + '/' + custom_vocab_file_name)
pp.pprint(response)

请等待 VocabularyState显示为READY,整个过程通常需要几分钟。具体参见以下代码:

# Wait for the status of the vocab you created to finishwhile True:

    response = transcribe.get_vocabulary(

        VocabularyName=vocab_improved

    )

    status = response['VocabularyState']

    if status in ['READY', 'FAILED']:

        print(status)

        break

    print("Not ready yet...")

    time.sleep(5)

在完成自定义词汇表的创建之后,您可以调用 transcribe 函数以启动另一项转录作业,并在其中使用自定义词汇表。具体参见以下代码:

job_name_custom_vid_0='AWS-custom-0-using-' + vocab_improved + str(time_now)

job_names_custom = [job_name_custom_vid_0]

transcribe(job_name_custom_vid_0, folder_path+all_videos[0], BUCKET, vocab_name=vocab_improved)

再次等待,直到您的转录作业状态显示为COMPLETED

使用以下代码,将新的转录结果保存为新的 .txt 文件:

i = 1for list_ in all_sentences_and_times_custom:   

    file = open(f"improved_transcript_{i}.txt","w")

    for tup in list_:

        file.write(tup['sentence'] + "\n") 

    file.close()

    i = i + 1

相关文章