11 遗传算法和遗传编程
在本章中,我们将学习遗传算法。 首先,我们将描述什么是遗传算法,然后将讨论进化算法和遗传编程的概念,并了解它们与遗传算法的关系。 我们将学习遗传算法的基本构建模块,包括交叉,变异和适应度函数。 然后,我们将使用这些概念来构建各种系统。
在本章结束时,您将对以下内容有更好的理解:
- 进化和遗传算法
- 遗传算法的基本概念
- 使用预定义参数生成位模式
- 可视化进化过程
- 解决符号回归问题
- 构建智能机器人控制器
进化论者流派
正如我们在书的开头提到的那样,研究遗传算法和遗传编程的计算机科学和数据科学研究人员是 Pedro Domingos 定义的进化论流派的一部分。 在某些方面,这个流派不在前面和中间。 连接主义者在阳光下度过一天,似乎在聚光灯下度过了愉快的时光。 正如 Domingos 博士所强调的那样,随着 CPU 的速度越来越快,并且在这一领域进行了更多的研究,如果在未来几年中出现新的,令人兴奋的前沿研究,不要感到惊讶。 他们已经在该领域做出了许多强大的创新性贡献,并将继续这样做。
了解进化和遗传算法
遗传算法是一种进化算法。 因此,为了理解遗传算法,我们需要讨论进化算法。 进化算法是一种运用启发式原理解决问题的元启发式优化算法。 进化的概念就像我们在自然界中发现的那样。 就像环境通过进化积极驱动“解决方案”一样,我们直接使用问题的函数和变量来得出解决方案。 但是在遗传算法中,任何给定的问题都以该算法操纵的位模式进行编码。
自主地解决问题是人工智能和机器学习的中心目标。 遗传算法(GA)是一种进化计算技术,可自动解决问题,而无需用户事先知道或指定解决方案的形式或结构。 从最抽象的层次上讲,GA 是计算机自动解决问题的一种系统的,与领域无关的方法,它从需要做什么的高级说明开始。
进化算法的基本步骤如下:
“步骤 1”
随机生成数据点或个体的初始种群。 由 GA 定义的个体是具有某些特征或特征的群体的成员。 在算法的后续步骤中,我们将确定这些特征是否使个体能够适应环境并生存足够长的时间以产生后代。
“步骤 2”
循环执行以下步骤,直到终止:
- 评估该群体中每个个体的健康状况。
- 选择最适合繁殖的个体。
- 通过交叉和变异操作育出新个体,以产生后代。
- 评估新个体的个体适应性。
- 用新的个体代替最不适合的群体。
使用预定义的适应度函数确定个体的适应度。 短语适者生存发挥作用。
然后,我们选择这些选定的个体,并通过重组和突变创建下一代个体。 我们将在下一部分中讨论重组和突变的概念。 现在,让我们将这些技术视为通过将选定的个体视为父代来创造下一代的机制。
一旦执行重组和突变,我们将创建一组新的个体,这些个体将与旧个体竞争下一代的位置。 通过抛弃最弱的个体并用后代代替它们,我们正在提高总体的整体适应水平。 我们继续进行迭代,直到达到所需的总体适应性。
遗传算法是一种进化算法,在该算法中,我们使用启发式算法来找到解决问题的字符串。 我们不断地对总体进行迭代,以找到解决方案。
我们通过产生包含更健康个体的新种群来做到这一点。 我们应用概率运算符,例如选择,交叉和突变,以便生成下一代个体。 个体用字符串表示,其中每个字符串都是潜在解决方案的编码版本。
使用适应性函数评估每个字符串的适应性度量,告诉我们解决问题的适用性。 该适应度函数也称为评估函数。 GA 会应用受自然启发的操作,这就是为什么该术语与生物生物学中发现的术语紧密相关的原因。
遗传算法的基本概念
为了建立 GA,我们需要了解几个关键概念和术语。 这些概念在 GA 的整个领域中得到广泛使用,以构建针对各种问题的解决方案。 GA 的最重要方面之一是随机性。 为了进行迭代,它依赖于对个体的随机采样。 这意味着该过程是不确定的。 因此,如果您多次运行相同的算法,则可能会得到不同的解决方案。
现在让我们定义术语总体。 总体是一组可能的候选解决方案。 在 GA 中,单个最佳解决方案不会在任何给定阶段维护,而是会保留一组潜在解决方案,其中一个可能是最佳解决方案。 但是其他解决方案在搜索过程中也起着重要作用。 由于跟踪解决方案的总体,因此不太可能陷入局部最优状态。 陷入局部最优是其他优化技术面临的经典问题。
现在,我们了解了 GA 的数量和随机性,下面我们来谈谈运算符。 当创建下一代个体时,算法会尝试确保它们来自当前世代中最适合的个体。
突变是实现此目的的一种方法。 遗传算法对当前的一个或多个个体进行随机更改,以产生新的候选解。 这种变化称为突变。 现在,这种变化可能会使该个体变得比现有个体更好或更糟。
需要定义的下一个概念是重组,也称为交叉。 这与繁殖在进化过程中的作用直接相关。 GA 试图将当前一代的个体合并起来,以创建新的解决方案。 它结合了每个父代个体的一些特征来创造这个后代。 此过程称为交叉。 目标是用总体中“钳工”个体产生的后代代替当前一代中的“较弱”个体。
为了应用交叉和突变,我们需要选择标准。 选择的概念受自然选择理论的启发。 在每次迭代期间,GA 都会执行选择过程。 优胜劣汰的个体使用此选择过程进行选择,较弱的个体被终止。 优胜劣汰概念的生存就在这里发挥作用。 使用计算i
个个体适应性的函数进行选择过程。
使用预定义参数生成位模式
现在我们知道了 GA 的基本概念,下面让我们看看如何使用它来解决一些问题。 我们将使用一个名为DEAP
的 Python 包。 您可以在这个页面中找到的所有详细信息。 让我们继续运行以下命令来安装它:
$ pip3 install deap
现在已经安装了该包,让我们快速对其进行测试。 通过键入以下命令进入 Python shell:
$ python3
进入内部后,键入以下内容:
>>> import deap
如果您没有看到错误消息,那就很好了。
在本节中,我们将使用, One Max 算法的变体。 One Max 算法尝试生成包含最大个数的位字符串。 这是一种简单的算法,但熟悉该库有助于更好地了解如何使用 GA 实现解决方案,这对您有所帮助。 在这种情况下,可能会生成包含预定义数量的 1 的位串。 您将看到底层结构和部分代码类似于DEAP
库中使用的示例。
创建一个新的 Python 文件并导入以下内容:
import random
from deap import base, creator, tools
假设我们要生成长度为 75 的位模式,并且希望它包含45
个位模式。 我们需要定义一个评估函数,可用于实现此目标:
# Evaluation function def eval_func(individual): target_sum = 45 return len(individual) - abs(sum(individual) - target_sum),
当个数等于45
时,前一个函数中使用的公式将达到最大值。 所有个体的长度为 75。当个数等于45
时,返回值为75
。
现在让我们定义一个函数来创建toolbox
。 首先,为适应性函数定义creator
对象并跟踪个体。 这里使用的Fitness
类是一个抽象类,它需要定义weights
属性。 我们正在使用正权重建立最大适应度:
# Create the toolbox with the right parameters def def create_toolbox(num_bits): creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax)
第一行创建一个最大化适应性的单一目标,名为FitnessMax
。 第二行涉及产生个体。 创建的第一个个体是浮点列表。 为了产生这个个体,我们必须使用creator
创建一个Individual
类。 适应性属性将使用之前定义的FitnessMax
。
toolbox
是DEAP
中常用的对象。 它用于存储各种函数及其参数。 让我们创建这个对象:
# Initialize the toolbox toolbox = base.Toolbox()
现在我们将开始向toolbox
注册各种函数。 让我们从生成0
和1
之间的随机整数的随机数生成器开始。 这基本上是生成位字符串:
# Generate attributes toolbox.register("attr_bool", random.randint, 0, 1)
让我们注册individual
函数。 方法initRepeat
带有三个参数–个体的容器类,用于填充容器的函数以及我们希望函数重复自身的次数:
# Initialize structures toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, num_bits)
我们需要注册population
函数。 我们希望总体成为个体列表:
# Define the population to be a list of individuals toolbox.register("population", tools.initRepeat, list, toolbox.individual)
现在,我们需要注册遗传运算符。 注册我们之前定义的evaluation
函数,它将用作适应函数。 我们希望个体(有点模式)有45
个:
# Register the evaluation operator toolbox.register("evaluate", eval_func)
使用cxTwoPoint
方法注册名为mate
的交叉运算符:
# Register the crossover operator toolbox.register("mate", tools.cxTwoPoint)
使用mutFlipBit
注册名为mutate
的变异运算符。 我们需要使用indpb
指定每个属性发生突变的概率:
# Register a mutation operator toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
使用selTournament
注册选择运算符。 它指定将选择哪些个体进行育种:
# Operator for selecting individuals for breeding toolbox.register("select", tools.selTournament, tournsize=3)
return toolbox
这是上一节中讨论的所有概念的实现。 DEAP
中常见的是toolbox
生成器函数,我们将在本章通篇使用它。 因此,花一些时间来了解toolbox
是如何产生的,这一点很重要。
从位模式的长度开始,通过定义main
函数:
if __name__ == "__main__": # Define the number of bits num_bits = 75
使用我们之前定义的函数创建一个toolbox
:
# Create a toolbox using the above parameter toolbox = create_toolbox(num_bits)
播种随机数生成器以获得可重复的结果:
# Seed the random number generator random.seed(7)
使用toolbox
对象中可用的方法来创建一个初始种群,例如500
个体。 随时更改此数字并进行试验:
# Create an initial population of 500 individuals population = toolbox.population(n=500)
定义交叉和变异的概率。 同样,这些是用户定义的参数。 因此,您可以更改这些参数并查看它们如何影响结果:
# Define probabilities of crossing and mutating probab_crossing, probab_mutating = 0.5, 0.2
定义迭代直到终止过程所需的代数。 如果增加世代数,则会给它更多的周期以提高种群的适应性:
# Define the number of generations num_generations = 60
使用适应度函数评估总体中的所有个体:
print('\nStarting the evolution process') # Evaluate the entire population fitnesses = list(map(toolbox.evaluate, population)) for ind, fit in zip(population, fitnesses): ind.fitness.values = fit
开始几代人迭代:
print('\nEvaluated', len(population), 'individuals') # Iterate through generations for g in range(num_generations): print("\n===== Generation", g)
在每一代中,使用我们之前注册到toolbox
的选择运算符选择下一代个体:
# Select the next generation individuals offspring = toolbox.select(population, len(population))
克隆选定的个体:
# Clone the selected individuals offspring = list(map(toolbox.clone, offspring))
使用先前定义的概率值,对下一代个体应用交叉和突变。 完成后,重置适应性值:
# Apply crossover and mutation on the offspring for child1, child2 in zip(offspring[::2], offspring[1::2]): # Cross two individuals if random.random() < probab_crossing: toolbox.mate(child1, child2)
# "Forget" the fitness values of the children del child1.fitness.values del child2.fitness.values
使用先前定义的相应概率值将变异应用于下一代个体。 完成后,重置适应性值:
# Apply mutation for mutant in offspring: # Mutate an individual if random.random() < probab_mutating: toolbox.mutate(mutant) del mutant.fitness.values
评估具有无效适应度值的个体:
# Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = map(toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit print('Evaluated', len(invalid_ind), 'individuals')
用下一代个体代替总体:
# The population is entirely replaced by the offspring population[:] = offspring
打印当前代的统计信息,以查看其的进度:
# Gather all the fitnesses in one list and print the stats fits = [ind.fitness.values[0] for ind in population] length = len(population) mean = sum(fits) / length sum2 = sum(x*x for x in fits) std = abs(sum2 / length - mean**2)**0.5 print('Min =', min(fits), ', Max =', max(fits)) print('Average =', round(mean, 2), ', Standard deviation =', round(std, 2)) print("\n==== End of evolution")
打印最终输出:
best_ind = tools.selBest(population, 1)[0] print('\nBest individual:\n', best_ind) print('\nNumber of ones:', sum(best_ind))
完整代码在文件bit_counter.py
中给出。 如果运行代码,将看到打印出的迭代。 开始时,您将看到类似以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWM9ReRA-1681568737501)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_01.png)]
图 1:初始演化输出(第 0 到第 3 代)
最后,您将看到类似于以下,它指示演进的结束:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pBcoErez-1681568737502)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_02.png)]
图 2:演化最终输出
如上图所示,进化过程在 60 代(零索引)之后结束。 完成后,将挑选最佳个体,并将其打印在输出中。 最佳个体中有 45 个,这是对的结果的确认,因为目标总和是评估函数中的45
。
可视化进化
让我们看看如何可视化演变过程。 在DEAP
中,有一种称为协方差矩阵适应演化策略(CMA-ES)的方法可以可视化演化。 是一种进化算法,用于解决连续域中的非线性问题。 CMA-ES 技术是可靠的,经过充分研究的,并且在进化算法中被认为是“最新技术”。 通过深入研究源代码来看看它是如何工作的。 以下代码是DEAP
库中显示的示例的微小变化。
创建一个新的 Python 文件并导入以下内容:
import numpy as np import matplotlib.pyplot as plt from deap import algorithms, base, benchmarks, \ cma, creator, tools
定义一个函数来创建toolbox
。 我们将使用负权重定义FitnessMin
函数:
# Function to create a toolbox def create_toolbox(strategy): creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", list, fitness=creator.FitnessMin)
创建toolbox
并注册评估函数,如下所示:
toolbox = base.Toolbox() toolbox.register("evaluate", benchmarks.rastrigin)
# Seed the random number generator np.random.seed(7)
注册generate
和update
方法。 这将使用生成-更新范式并从策略生成总体,然后根据总体更新策略:
toolbox.register("generate", strategy.generate, creator.Individual) toolbox.register("update", strategy.update)
return toolbox
定义main
函数。 首先定义个体数量和世代数量:
if __name__ == "__main__": # Problem size num_individuals = 10 num_generations = 125
在开始该过程之前,定义一个strategy
:
# Create a strategy using CMA-ES algorithm strategy = cma.Strategy(centroid=[5.0]*num_individuals, sigma=5.0, lambda_=20*num_individuals)
根据策略创建toolbox
:
# Create toolbox based on the above strategy toolbox = create_toolbox(strategy)
创建一个HallOfFame
对象。 HallOfFame
对象包含群体中存在的最佳个体。 该对象始终保持排序格式。 这样,此对象中的第一个元素就是在进化过程中具有最佳适应性值的个体:
# Create hall of fame object hall_of_fame = tools.HallOfFame(1)
使用Statistics
方法注册统计信息:
# Register the relevant stats stats = tools.Statistics(lambda x: x.fitness.values) stats.register("avg", np.mean) stats.register("std", np.std) stats.register("min", np.min) stats.register("max", np.max)
定义logbook
以跟踪演变记录。 它基本上是按时间顺序排列的词典列表:
logbook = tools.Logbook() logbook.header = "gen", "evals", "std", "min", "avg", "max"
定义对象以编译所有数据:
# Objects that will compile the data sigma = np.ndarray((num_generations, 1)) axis_ratio = np.ndarray((num_generations, 1)) diagD = np.ndarray((num_generations, num_individuals)) fbest = np.ndarray((num_generations,1)) best = np.ndarray((num_generations, num_individuals)) std = np.ndarray((num_generations, num_individuals))
遍历几代人:
for gen in range(num_generations): # Generate a new population population = toolbox.generate()
使用适应度函数评估个体:
# Evaluate the individuals fitnesses = toolbox.map(toolbox.evaluate, population) for ind, fit in zip(population, fitnesses): ind.fitness.values = fit
根据总体更新策略:
# Update the strategy with the evaluated individuals toolbox.update(population)
与当前这一代人一起更新名人堂和统计数据:
# Update the hall of fame and the statistics with the # currently evaluated population hall_of_fame.update(population) record = stats.compile(population) logbook.record(evals=len(population), gen=gen, **record) print(logbook.stream)
保存绘图的数据:
# Save more data along the evolution for plotting sigma[gen] = strategy.sigma axis_ratio[gen] = max(strategy.diagD)**2/min(strategy.diagD)**2 diagD[gen, :num_individuals] = strategy.diagD**2 fbest[gen] = hall_of_fame[0].fitness.values best[gen, :num_individuals] = hall_of_fame[0] std[gen, :num_individuals] = np.std(population, axis=0)
定义x
轴并绘制统计数据:
# The x-axis will be the number of evaluations x = list(range(0, strategy.lambda_ * num_generations, strategy.lambda_)) avg, max_, min_ = logbook.select("avg", "max", "min") plt.figure() plt.semilogy(x, avg, "--b") plt.semilogy(x, max_, "--b") plt.semilogy(x, min_, "-b") plt.semilogy(x, fbest, "-c") plt.semilogy(x, sigma, "-g") plt.semilogy(x, axis_ratio, "-r") plt.grid(True) plt.title("blue: f-values, green: sigma, red: axis ratio")
绘制进度:
plt.figure() plt.plot(x, best) plt.grid(True) plt.title("Object Variables")
plt.figure() plt.semilogy(x, diagD) plt.grid(True) plt.title("Scaling (All Main Axes)")
plt.figure() plt.semilogy(x, std) plt.grid(True) plt.title("Standard Deviations in All Coordinates") plt.show()
完整代码在文件visualization.py
中给出。 如果运行代码,您将看到四个屏幕截图。 第一个屏幕截图显示了各种参数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pVTgGmte-1681568737503)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_03.png)]
图 3:演变过程中绘制的参数
第二张屏幕截图显示了对象变量:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcJ2yy5I-1681568737503)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_04.png)]
图 4:绘制演化过程中的对象变量
第三张屏幕快照显示缩放:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NB0Jy61y-1681568737503)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_05.png)]
图 5:演变过程中的标绘比例
第四个屏幕截图显示了标准差:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tazY0OmJ-1681568737504)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_06.png)]
图 6:从进化过程中绘制的标准差
您将看到打印出来的进度。 首先,您会看到类似以下的内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MptKxjgx-1681568737504)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_07.png)]
图 7:演化过程的初始输出
最后,您将看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCSwGKt0-1681568737504)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_08.png)]
图 8:演进进度最终输出
从上图可以看出,随着我们的前进,所有值都使 d 不断增加。 中的内容表明它正在收敛。
解决符号回归问题
在本章的最后,我们将看到 GA 在众多行业和领域中的许多应用。 从财务到流量优化,GA 的应用几乎是无止境的。 不过,到目前为止,我们继续另一个简单的示例。 让我们看看如何使用遗传编程来解决符号回归问题。 重要的是要了解遗传程序设计与 GA 并不相同。 遗传编程是一种进化算法,其中解决方案以计算机程序的形式出现。 每代人都是计算机程序,他们的适应水平与其解决问题的能力相对应。 每次迭代时,都使用 GA 修改这些程序。 遗传程序设计是 GA 的应用。
关于符号回归问题,我们有一个多项式表达式,在这里需要近似。 这是一个经典的回归问题,我们尝试估计基本函数。 在此示例中,我们将使用表达式:f(x) = 2x ^ 3 – 3x ^ 2 + 4x – 1
。
此处讨论的代码是DEAP
库中给出的符号回归问题的变体。 创建一个新的 Python 文件并导入以下内容:
import operator import math import random
import numpy as np from deap import algorithms, base, creator, tools, gp
创建一个除法运算符,可以优雅地处理被零除的错误:
# Define new functions def division_operator(numerator, denominator): if denominator == 0: return 1
return numerator / denominator
定义将用于适应度计算的评估函数。 我们需要定义一个可调用函数以对输入个体进行计算:
# Define the evaluation function def eval_func(individual, points): # Transform the tree expression in a callable function func = toolbox.compile(expr=individual)
计算先前定义的函数和原始表达式之间的均方误差(MSE):
# Evaluate the mean squared error mse = ((func(x) - (2 * x**3 - 3 * x**2 + 4 * x - 1))**2 for x in points)
return math.fsum(mse) / len(points),
定义一个函数来创建 toolbox
。 为了在此处创建toolbox
,需要创建一组原语。 这些原语是将在演化过程中使用的运算符。 它们是个体的基石。 这些原语将是基本的算术函数:
# Function to create the toolbox def create_toolbox(): pset = gp.PrimitiveSet("MAIN", 1) pset.addPrimitive(operator.add, 2) pset.addPrimitive(operator.sub, 2) pset.addPrimitive(operator.mul, 2) pset.addPrimitive(division_operator, 2) pset.addPrimitive(operator.neg, 1) pset.addPrimitive(math.cos, 1) pset.addPrimitive(math.sin, 1)
接下来,声明一个临时常量。 临时常数是一种特殊的终端类型,没有固定值。 当给定程序将此类临时常量附加到树上时,该函数将被执行。 然后将结果作为常数终端插入树中。
这些常量端子可以采用-1
,0
或1
值:
pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
参数的默认名称为ARGx
。 让我们将其重命名为x
:
pset.renameArguments(ARG0='x')
我们需要定义两种对象类型–适应度和个体。 让我们用creator
来做:
creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
创建toolbox
和register
函数。 注册过程的完成类似于前面的部分:
toolbox = base.Toolbox()
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2) toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr) toolbox.register("population", tools.initRepeat, list, toolbox.individual) toolbox.register("compile", gp.compile, pset=pset) toolbox.register("evaluate", eval_func, points=[x/10\. for x in range(-10,10)]) toolbox.register("select", tools.selTournament, tournsize=3) toolbox.register("mate", gp.cxOnePoint) toolbox.register("expr_mut", gp.genFull, min_=0, max_=2) toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
toolbox.decorate("mate", gp.staticLimit(key=operator.attrgetter("height"), max_value=17)) toolbox.decorate("mutate", gp.staticLimit(key=operator.attrgetter("height"), max_value=17))
return toolbox
定义main
函数并从播种随机数生成器开始:
if __name__ == "__main__": random.seed(7)
创建toolbox
对象:
toolbox = create_toolbox()
使用450
个体使用toolbox
对象中可用的方法定义初始种群。 人数可以更改。 随时尝试。 还定义hall_of_fame
对象:
population = toolbox.population(n=450) hall_of_fame = tools.HallOfFame(1)
建立 GA 时,统计数据非常有用。 定义统计对象:
stats_fit = tools.Statistics(lambda x: x.fitness.values) stats_size = tools.Statistics(len)
使用先前定义的对象注册统计信息:
mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size) mstats.register("avg", np.mean) mstats.register("std", np.std) mstats.register("min", np.min) mstats.register("max", np.max)
定义交叉概率,突变概率和世代数:
probab_crossover = 0.4 probab_mutate = 0.2 num_generations = 60
使用以上参数运行进化算法:
population, log = algorithms.eaSimple(population, toolbox, probab_crossover, probab_mutate, num_generations, stats=mstats, halloffame=hall_of_fame, verbose=True)
完整代码在文件symbol_regression.py
中给出。 如果运行代码,则在演化开始时将看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7ewzF8w-1681568737505)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_09.png)]
图 9:演化过程的初始输出
最后,您将看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BC1Inz2A-1681568737505)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_10.png)]
图 10:演进进度最终输出
我们可以看到min
列中的值越来越小,这表明方程的近似解的误差越来越小。
构建智能机器人控制器
让我们看看如何使用 GA 构建机器人控制器。 我们得到了一张地图,上面洒满了目标。
地图看起来像这样。 哈希表示机器人要击中所需的目标:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6gq9Izt-1681568737505)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_11.png)]
图 11:带有 AI 机器人需要击中的目标的地图,目标以哈希表示
前面的地图中有 124 个目标。 机器人控制器的目标是自动遍历地图并消耗所有这些目标。 该程序是deap
库中提供的人工蚂蚁程序的变体。
创建一个新的 Python 文件并导入以下内容:
import copy import random from functools import partial import numpy as np from deap import algorithms, base, creator, tools, gp
创建用于控制机器人的类:
class RobotController(object): def __init__(self, max_moves): self.max_moves = max_moves self.moves = 0 self.consumed = 0 self.routine = None
定义方向和运动:
self.direction = ["north", "east", "south", "west"] self.direction_row = [1, 0, -1, 0] self.direction_col = [0, 1, 0, -1]
定义重置函数:
def _reset(self): self.row = self.row_start self.col = self.col_start self.direction = 1 self.moves = 0 self.consumed = 0 self.matrix_exc = copy.deepcopy(self.matrix)
定义条件控制器:
def _conditional(self, condition, out1, out2): out1() if condition() else out2()
定义左转弯控制器:
def turn_left(self): if self.moves < self.max_moves: self.moves += 1 self.direction = (self.direction - 1) % 4
定义正确的右转控制器:
def turn_right(self): if self.moves < self.max_moves: self.moves += 1 self.direction = (self.direction + 1) % 4
定义控制机器人前进方式的方法:
def move_forward(self): if self.moves < self.max_moves: self.moves += 1 self.row = (self.row + self.direction_row[self.direction]) % self.matrix_row self.col = (self.col + self.direction_col[self.direction]) % self.matrix_col if self.matrix_exc[self.row][self.col] == "target": self.consumed += 1
self.matrix_exc[self.row][self.col] = "passed"
定义一种检测目标的方法。 如果您看到前方的目标,请相应地更新矩阵:
def sense_target(self): ahead_row = (self.row + self.direction_row[self.direction]) % self.matrix_row ahead_col = (self.col + self.direction_col[self.direction]) % self.matrix_col return self.matrix_exc[ahead_row][ahead_col] == "target"
如果看到前面的目标,则创建相关函数并返回它:
def if_target_ahead(self, out1, out2): return partial(self._conditional, self.sense_target, out1, out2)
定义运行它的方法:
def run(self,routine): self._reset() while self.moves < self.max_moves: routine()
定义一个函数以遍历输入图。 符号#
指示地图上的所有目标,符号S
指示起点。 符号.
表示空白单元格:
def traverse_map(self, matrix): self.matrix = list() for i, line in enumerate(matrix): self.matrix.append(list())
for j, col in enumerate(line): if col == "#": self.matrix[-1].append("target")
elif col == ".": self.matrix[-1].append("empty")
elif col == "S": self.matrix[-1].append("empty") self.row_start = self.row = i self.col_start = self.col = j self.direction = 1
self.matrix_row = len(self.matrix) self.matrix_col = len(self.matrix[0]) self.matrix_exc = copy.deepcopy(self.matrix)
定义一个类以根据输入参数的数量生成函数:
class Prog(object): def _progn(self, *args): for arg in args: arg()
def prog2(self, out1, out2): return partial(self._progn, out1, out2)
def prog3(self, out1, out2, out3): return partial(self._progn, out1, out2, out3)
为个体定义评估函数:
def eval_func(individual): global robot, pset
# Transform the tree expression to functional Python code routine = gp.compile(individual, pset)
运行程序:
# Run the generated routine robot.run(routine) return robot.consumed,
定义一个函数来创建toolbox
并添加原语:
def create_toolbox(): global robot, pset pset = gp.PrimitiveSet("MAIN", 0) pset.addPrimitive(robot.if_target_ahead, 2) pset.addPrimitive(Prog().prog2, 2) pset.addPrimitive(Prog().prog3, 3) pset.addTerminal(robot.move_forward) pset.addTerminal(robot.turn_left) pset.addTerminal(robot.turn_right)
使用适应度函数创建对象类型:
creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMax)
创建toolbox
并注册所有运算符:
toolbox = base.Toolbox()
# Attribute generator toolbox.register("expr_init", gp.genFull, pset=pset, min_=1, max_=2)
# Structure initializers toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr_init) toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("evaluate", eval_func) toolbox.register("select", tools.selTournament, tournsize=7) toolbox.register("mate", gp.cxOnePoint)
toolbox.register("expr_mut", gp.genFull, min_=0, max_=2) toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
return toolbox
定义main
函数,并从播种随机数生成器开始:
if __name__ == "__main__": global robot
# Seed the random number generator random.seed(7)
使用初始化参数创建机械手控制器对象:
# Define the maximum number of moves max_moves = 750
# Create the robot object robot = RobotController(max_moves)
使用我们之前定义的函数创建toolbox
:
# Create the toolbox toolbox = create_toolbox()
从输入文件中读取地图数据:
# Read the map data with open('target_map.txt', 'r') as f: robot.traverse_map(f)
用400
个体定义总体并定义hall_of_fame
对象:
# Define population and hall of fame variables population = toolbox.population(n=400) hall_of_fame = tools.HallOfFame(1)
注册stats
:
# Register the stats stats = tools.Statistics(lambda x: x.fitness.values) stats.register("avg", np.mean) stats.register("std", np.std) stats.register("min", np.min) stats.register("max", np.max)
定义交叉概率,突变概率和世代数:
# Define parameters probab_crossover = 0.4 probab_mutate = 0.3 num_generations = 50
使用前面定义的参数运行进化算法:
# Run the algorithm to solve the problem algorithms.eaSimple(population, toolbox, probab_crossover, probab_mutate, num_generations, stats, halloffame=hall_of_fame)
完整代码在文件robot.py
中给出。 如果运行代码,您将获得以下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TDtbbsIC-1681568737505)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_12.png)]
图 12:演化过程的初始输出
在结束时,您将看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BD25KJAt-1681568737506)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_13.png)]
图 13:演进过程的最终输出
从上图中可以看出,随着我们的发展,标准差不断减小。 这表明它正在收敛。 在此输出中,我们仅显示 50 代。 如果运行更多的代,您可以期望这些值甚至会进一步收敛。
遗传编程用例
正如早期章节之一所讨论的那样,遗传算法(GA)和遗传编程(GP)是机器学习的“五个流派”之一。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vZgSUPtK-1681568737506)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_11_14.png)]
图 14:五个流派(佩德罗·多明戈斯)
从一开始,GP 就取得了各种各样的进步。 涵盖 GP 数千种应用的文献包含许多成功应用 GP 的用例。 详尽地涵盖该列表将超出本书的范围,但是我们在此处列出了一些更重要的列表。
在这里,我们开始讨论 GP 已成功应用的一般问题的讨论,然后回顾 GP 每个主要应用领域的代表性子集。 根据多年来不同研究人员的经验,GP 做得好的领域包括:
域理解不佳
这是相关变量之间的相互关系未知或了解不充分的地方(或者怀疑当前的理解可能是错误的)。 GP(和其他进化算法)的好处之一是探索了人们不了解的领域。 如果对问题领域有充分的了解,则可以使用其他分析工具和方法来提供高质量的解决方案,而不会在 GP 的随机搜索过程中固有的不确定性。
另一方面,当对该域的了解不充分时,GP 会产生结果。 GP 可以帮助确定哪些属性和维度是相关的,提供新颖和创新的解决方案,揭示属性之间的意外关系,并发现可以应用于其他领域的新概念。
找到最终解决方案的大小和形状是问题的主要部分。 如果知道解决方案的形式,那么用于固定大小表示形式的替代搜索机制(例如 GA)可能会更有效,因为它们不必发现解决方案的大小和形状。
数据可用并且很多
特别是 GP,通常是机器学习和搜索技术,通常需要执行大量测试数据。 寻找问题的相关数据集可能是一大障碍。 但是,在大数据集随时可用的情况下,这可能是数据科学家的梦想,提出仅仅因为数据可用而提出的问题可能是一个好主意。
如果测试数据尽可能干净和准确,这也很有帮助。 但是,GP 算法可以很好地处理数据中的一定数量的噪声,尤其是在采取措施将过拟合最小化的情况下。
可接受近似解决方案
GP 在近似解决方案可以接受或最佳解决方案的情况下效果很好。 总体而言,尤其是 GP 的演变通常意味着“足够好”而不是“最好”。 如果一只熊在树林中追赶您,您不必成为世界上最快的人。 您只需要比熊或奔跑的人快。 结果,进化算法往往在可能且可接受的近似值接近最佳的领域中工作最佳。
小但有价值的改进
已发现 GP 在技术努力往往集中在经济重要性较高的领域中效果很好。 在这些领域中,以前的研究人员可能花费了大量时间和精力,并且“最新技术”趋于先进。 在这种情况下,很难改善当前的解决方案。 但是,在这些相同的领域中,小的改进可能非常有价值。 在这种情况下,GP 有时会做出很小但有价值的贡献。 例如石油探索,材料管理和财务应用。
现在,让我们看一下 GA 和 GP 的一些特定于行业的应用:
电影
和其他职业一样,电影特技演员的日子也数不清了。 一家名为 NaturalMotion 的初创公司使用 GP 产生了令人难以置信的逼真的效果,从而使人动起来。 这些虚拟演员以真实的精度跌倒,跳跃并表演其他特技。 这些虚拟角色可以像真实的人类一样对施加到其上的力做出反应,并表现出各种各样逼真的动作。 所有这些,仅需要台式机的功能。 电影只是开始。 在接下来的几年中,NaturalMotion 计划在下一代视频游戏中释放这些逼真的人物。
NaturalMotion 是由前牛津研究人员 Torsten Reil 和 Colm Massey 创立的一家新公司。 目前,该公司只有一种产品,称为 Endorphin,它利用神经网络和人工进化技术来产生可以像人一样精确地行走,奔跑,跌倒和飞行的软件自动机。
Endorphin 在电影《王者归来》中首次亮相,这是用来使特别棘手的特技栩栩如生的电影。 但这仅仅是开始。 几个月后,该公司的机器人在 Ilium 平原上战斗致死,摔倒了沃尔夫冈·彼得森(Wolfgang Petersen)的电影 Troy 并摔倒了匕首。
来源: https://www.naturalmotion.com/
电脑游戏
今天,每个体都对深度学习算法着迷。 他们肯定在许多领域和许多基准下都取得了令人印象深刻的结果。 但是 GP 并不懈怠。 由于丹尼斯·威尔逊(Dennis Wilson)和法国图卢兹大学的一些同事的努力,已经观察到了令人印象深刻的结果。 他们在 GP 上所做的工作在许多经典游戏中都能够胜过人类。 威尔逊和他的研究人员团队展示了 GP 如何在具有象征意义的任务上与深度学习算法的表现相媲美,这项任务使深度学习在 2013 年成名–在 Pong,Breakout 和 Space Invaders 等街机视频游戏中,人类的表现优于人类。
威尔逊令人信服地证明,GP 可以产生可比的令人印象深刻的结果,甚至可能比深度学习更好。
文件压缩
最早的无损压缩技术之一是使用 GP 演变非线性图像预测器。 该算法根据相邻像素子集的灰度值预测像素可以采用的灰度级。 结合模型描述的预测误差可以表示图像的压缩版本。 使用霍夫曼编码对图像进行压缩。 使用 GP 压缩,各种图像上的结果都显示出令人鼓舞的结果。 在某些情况下,GP 算法的表现优于某些人为设计的最佳无损压缩算法。
资料来源:Fukunaga 和 Stechert,1998[1]
金融交易
有效市场的假设是经济学的基本原理。 它基于这样的思想:每个市场参与者都具有完美的信息,并且他们理性地发挥作用。 如果有效市场假说是正确的,那么每个体都应该为市场中的所有资产分配相同的价格,并就价格达成一致。 如果不存在价格差异,就没有办法击败市场。 无论是商品市场,货币市场还是股票市场,没有任何一个市场参与者是平等的,并且存在很大的疑问,即有效市场是否确实存在。 市场流动性越弱,市场效率就越低。 因此,人们继续研究股票市场并试图找到击败股票市场的方法。 有一些人和公司基于他们的往绩证明了市场是可战胜的。 一些示例包括:
- 沃伦·巴菲特(Warren Buffet)和伯克希尔·哈撒韦(Berkshire Hathaway)
- 彼得·林奇和富达麦哲伦基金
- 雷·达里奥(Ray Dalio)和 Bridgewater Associates
- 吉姆·西蒙斯(Jim Simons)和 Renaissance Technologies
后两个示例严重依赖计算机算法来获得市场领先的结果。
博弈论一直是经济学家试图了解市场的一种标准工具,但是越来越多的人为和计算机化的智能体进行了模拟。 GP 被越来越多地用作这些社会系统模拟的一部分。
GP 算法广泛用于金融交易,时间序列预测和经济建模领域; 一整本书要列出它的所有应用。
在本节中,我们将从头开始,并访问一些示例。 在这一领域特别杰出的研究人员是陈胜雄。 Chen 写了 60 多篇关于在金融和经济学中使用 GP 的论文。 他最近的一些论文研究了股票市场中的智能体建模(Chen 和 Liao,2005),博弈论(Chen,Duffy 和 Yeh,2002),标准普尔 500 指数交易规则的演变(Yu 和 Chen,2004)。 并预测了恒生指数(Chen,Wang 和 Zhang,1999 年)。
其他应用
优化:GA 和 GP 通常用于优化问题,在给定目标函数的情况下,必须在一组约束条件下将值最大化或最小化。
并行化:GA 也具有并行处理功能,并且被证明是解决需要并行处理的问题的有效方法。 并行化是 GA 和 GP 研究的活跃领域。
神经网络:GA 用于训练神经网络,尤其是循环神经网络(RNN)。
经济学:GA 通常用于对经济系统进行建模,例如:
- 蜘蛛网模型
- 博弈论均衡解
- 资产定价
图像处理:GA 也用于各种数字图像处理(DIP)任务,例如密集像素匹配。
调度应用:GA 可用于解决许多调度问题,尤其是时间表问题。 简而言之,当我们拥有一组资源,一组活动以及活动与资源之间的依赖关系时,就会发生时间表问题。 一个例子是在我们有教室,教授和学生的大学中的课程表,并且在练习结束时,希望很大比例的学生能够参加他们想参加的所有课程。
参数化设计:GA 已用于通过更改参数和发展更好的解决方案来设计车辆,机械和飞机。
DNA 分析:GA 可以并且已经用于使用样本谱数据确定 DNA 结构。
多峰优化:GA 是解决寻求多个最优解的多峰优化问题的好方法。
旅行商问题(TSP):遗传算法已用于解决 TSP 及其所有相关应用,例如车辆路线和机器人轨迹问题,这是一种使用新颖交叉法和包装策略的广为人知的组合我呢提。
希望 GP 和 GA 的广泛而多样的应用为您所熟悉。 也许您将能够提出自己独特的应用,并利用获得的知识来推动该领域的发展。
总结
在本章中,我们了解了 GA 及其基本概念。 我们讨论了进化算法和遗传规划。 我们了解了它们与 GA 之间的关系。 我们讨论了 GA 的基本构建模块,包括种群,交叉,突变,选择和适应度函数的概念。 我们学习了如何使用预定义的参数生成位模式。 我们讨论了如何使用 CMA-ES 可视化演变过程。 我们学习了如何在此范例中解决符号回归问题。 然后,我们使用这些概念来构建机器人控制器,以遍历地图并消耗所有目标。 在下一章中,我们将学习强化学习,并了解如何构建智能体。
参考
A. Fukunaga and A. Stechert. Evolving nonlinear predictive models for lossless image compression with genetic programming. In J. R. Koza, et al., editors, Genetic Programming 1998: Proceedings of the Third Annual Conference, pages 95-102, University of Wisconsin, Madison, Wisconsin, USA, 22-25 July 1998. Morgan Kaufmann. ISBN 1-55860-548-7.
12 云上的人工智能
在本章中,我们将学习有关云和云上的人工智能工作负载的信息。 我们将讨论将 AI 项目迁移到云的好处和风险。 我们还将了解主要云提供商所提供的产品。 我们将了解他们提供的服务和功能,并希望了解为什么这些提供商是市场领导者。
在本章结束时,您将对以下内容有更好的理解:
- 迁移到云的好处,风险和成本
- 基本云概念(例如弹性)
- 顶级云提供商
- 亚马逊网络服务:
- 亚马逊 SageMaker
- Alexa,Lex 和 Polly – 对话智能体
- Amazon Comprehend – 自然语言处理
- Amazon Rekognition – 图片和视频
- 亚马逊翻译
- 亚马逊机器学习
- 亚马逊 Transcribe – 转录
- Amazon Textract – 文档分析
- Microsoft Azure:
- 机器学习工作室
- Azure 机器学习交互式工作区
- Azure 认知服务
- Google AI 及其机器学习产品:
- AI 中心
- 人工智能构建块
为什么公司要迁移到云?
如今,很难在不被“云”一词影响的情况下转向任何地方。 我们当今的社会已经达到了一个临界点,无论大小企业都看到将工作负载转移到云中所带来的好处超过了成本和风险。 例如,截至 2019 年,美国国防部正在选择一家云提供商并授予 10 年 100 亿美元的合同。 将您的系统迁移到云具有许多优势,但是公司迁移到云的主要原因之一是其弹性功能。
在本地环境中部署新项目时,我们总是从容量规划开始。 容量规划是企业进行的一项练习,以确定为使新系统有效运行所需的硬件数量。 根据项目的规模,这种硬件的成本可能高达数百万美元。 因此,可能需要几个月的时间才能完成该过程。 可能需要很长时间的原因之一是,可能需要许多批准才能完成购买。 我们不能责怪企业对此类决策如此迟钝和明智。
尽管可能需要进行周密的计划和考虑,但购买少于所需数量的设备或购买动力不足的设备并不少见。 也许就像经常那样,购买了过多的设备或对眼前的项目而言过高的设备。 发生这种情况的原因是,在许多情况下,很难先验确定需求。
此外,即使我们在一开始就获得了适当的容量要求,需求可能仍会继续增长,并迫使我们重新进行供应流程。 否则需求可能会变化。 例如,我们的网站白天可能会吸引大量流量,但晚上的需求却下降了。 在这种情况下,当使用本地环境时,我们别无选择,只能考虑最坏的情况并购买足够的资源,以便我们可以处理需求高峰期,但是当需求在缓慢时期减少时,资源将被浪费。
所有这些问题在云环境中都不存在。 所有主要的云提供商都以不同的方式提供弹性的环境。 我们不仅可以轻松扩展,而且可以轻松扩展。
如果我们有一个流量可变的网站,则可以将处理流量的服务器放在负载均衡器后面,并设置警报,以自动添加更多服务器以处理流量高峰,并在风暴过后自动添加其他警报以终止服务器。
顶级云提供商
鉴于云是海啸,许多供应商都在争相降低对云服务的需求。 但是,就像在技术市场中经常发生的那样,只有极少数公司冒顶并占据主导地位。 在本节中,我们将分析排名靠前的参与者。
亚马逊网络服务(AWS)
Amazon Web Services 是云计算的先驱之一。 自 2006 年推出以来,AWS 在愿景和执行力方面一直在备受推崇的 Gartner 的 Magic 象限中排名很高。 自成立以来,AWS 占据了很大的云市场份额。 对于传统参与者和初创企业而言,AWS 都是一个有吸引力的选择。 根据 Gartner:
“AWS 是战略性,全组织范围采用的最常用的提供商”
AWS 还拥有一支由顾问组成的大军,致力于帮助其客户部署 AWS 服务,并教他们如何最佳利用可用服务。 综上所述,可以肯定地说,AWS 是最成熟,最先进的云提供商,在客户成功方面拥有良好的往绩,并且在 AWS Marketplace 中拥有强大的合作伙伴。
另一方面,由于 AWS 是领导者并且他们知道,因此它们并非总是最便宜的选择。 AWS 的另一个问题是,由于他们非常珍视将新服务和功能首先推向市场,因此他们似乎愿意迅速推出可能尚未完全成熟和功能完善的服务,并在发布后立即解决问题 。 公平地说,这不是 AWS 独有的策略,其他云提供商也发布了其服务的 Beta 版本。 此外,由于亚马逊在云计算以外的市场竞争,因此一些潜在客户与其他提供商合作以免“喂食野兽”并不少见。 例如,沃尔玛以避免不惜一切代价使用 AWS 而闻名,因为它们在电子商务领域竞争激烈。
Microsoft Azure
在过去的几年中,Microsoft Azure 在 Gartner 魔力象限中排名第二,仅次于 AWS,其执行能力要远远优于 AWS。 但是好消息是,它们仅落后于 AWS,而且排名第二。
微软的解决方案吸引托管旧式工作负载以及全新的云部署的客户,但出于不同的原因。
传统的工作负载通常由传统上是 Microsoft 客户的客户在 Azure 上运行,并试图利用他们以前在该技术栈中的投资。
对于新的云部署,由于 Microsoft 为应用开发提供了强大的产品,专业的平台即服务(PaaS)功能,数据存储,机器学习和物联网(IoT)服务,Azure 云服务吸引人们。
在战略上致力于 Microsoft 技术栈的企业已经能够在生产中部署许多大型应用。 当开发人员完全致力于 Microsoft 产品套件(例如.NET 应用),然后将其部署在 Azure 上时,Azure 尤其有用。 微软之所以能够深入市场,是因为其经验丰富的销售人员和广泛的合作伙伴网络。
此外,微软意识到,下一轮技术战将不会围绕操作系统展开,而是会围绕云进行,因此它们已越来越多地接受采用非微软操作系统。 为了证明这一点,到目前为止,大约一半的 Azure 工作负载运行在 Linux 或其他开源操作系统和技术栈上。
Gartner 的一份报告指出:“微软对未来具有独特的愿景,涉及通过本机的第一方产品(例如来自 VMware,NetApp,Red Hat,Cray 和 Databricks 的产品)引入技术合作伙伴。”
不利的一面是,有一些关于可靠性,停机时间和服务中断的报告,还有一些客户对 Microsoft 技术支持的质量表示怀疑。
Google 云平台(GCP)
在 2018 年,Google 通过其 GCP 产品将打破了享誉全球的 Gartner 领导者象限,仅加入了 AWS 和 Azure 的独家俱乐部。 在 2019 年,GCP 与两个激烈的竞争对手保持在同一象限。 但是,就市场份额而言,GCP 仅排在第三位。
他们最近加强了销售人员,他们有足够的财力,并且有强烈的动机要不落伍,所以不要折扣扣。
Google 作为机器学习领导者的声誉无可争议,因此 GCP 拥有强大的大数据和机器学习产品也就不足为奇了。 但是 GCP 也在取得进展,吸引了较大的企业,这些企业希望托管诸如 SAP 和其他传统客户关系管理(CRM)系统之类的传统工作负载。
Google 在机器学习,自动化,容器和网络方面的内部创新以及 TensorFlow 和 Kubernetes 等产品具有先进的云开发能力。 GPS 的技术围绕着对开源的贡献。
但是,请注意将云策略专门围绕 GCP 集中。 Gartner 在最近的一份报告中宣称:
“谷歌在处理企业账户时表现出不成熟的流程和程序,这有时会使公司难以交易。”
和:
“与本魔力象限中的其他供应商相比,谷歌拥有的经验丰富的托管服务提供商(MSP)和以基础架构为中心的专业服务合作伙伴要少得多。”
但是,Gartner 还指出:
“ Google 正积极针对这些缺点。”
Gartner 还指出,谷歌的渠道需要发展。
阿里云
阿里云于 2017 年首次出现在 Gartner 的魔力象限中,而于 2019 年首次出现在 Gartner 的魔力象限中。
Gartner 仅评估了总部位于新加坡的公司的国际服务。
阿里云是中国市场的领导者,使用阿里巴巴作为云提供商,为许多中国企业以及中国政府提供了良好的服务。 但是,如果中国决定取消对其他国际云供应商的某些限制,则可能会放弃这一市场份额领导地位的很大一部分。
该公司在中国为构建混合云提供支持。 但是,在中国以外,它主要用于以云为中心的工作负载。 2018 年,它与 VMware 和 SAP 建立了合作伙伴关系。
阿里巴巴拥有一套服务,其范围可与其他全球提供商的服务组合相媲美。
该公司与阿里巴巴集团的紧密关系帮助云服务成为希望在中国开展业务的国际公司和中国公司在中国以外的公司的桥梁。
阿里巴巴似乎还没有拥有竞争对手 AWS,Azure 和 GCP 等等竞争对手的服务和功能。 在许多地区,服务仅可用于特定的计算实例。 他们还需要加强其 MSP 生态系统,第三方企业软件集成和操作工具。
Oracle 云基础架构(OCI)
在 2017 年,Oracle 的云产品在 Gartner 的 Magic 象限象限中首次亮相。 但是在 2018 年,由于 Gartner 评估标准的变更,甲骨文被提升为 Niche Player 地位。 截至 2019 年,它一直在那里。
Oracle 云基础架构(OCI)是于 2016 年推出的第二代服务,旨在淘汰旧版产品(现称为 Oracle Cloud Infrastructure Classic)。
OCI 同时提供虚拟服务器和裸机服务器,并一键式安装和配置 Oracle 数据库和容器服务。
OCI 吸引具有 Oracle 工作负载的客户,这些工作负载只需要基本的基础架构即服务(IaaS)功能。
Oracle 的云战略依赖于其应用,数据库和中间件。
Oracle 在吸引其他云提供商的人才以增强其产品方面取得了一些进展。 它还在赢得新业务和使现有的 Oracle 客户转移到 OCI 云方面取得了一些进展。 但是,Oracle 在赶上三巨头之前还有很长的路要走。
IBM Cloud
在大型机时代,IBM 是无可争议的计算之王。 当我们开始脱离大型机,而个人计算机无处不在时,它就失去了这个头衔。 IBM 再次试图在这一新的范式转变中重新占据领导地位。 IBM Cloud 是 IBM 应对这一挑战的答案。
该公司的多元化云服务包括容器平台,无服务器服务和 PaaS 产品。 IBM Cloud Private 为混合架构提供了补充。
像其他一些较低层的云提供商一样,IBM 吸引了其现有客户,这些客户非常愿意从 Big Blue(IBM 的昵称)购买大部分技术。
这些现有客户通常具有传统的工作负载。 IBM 还利用这些长期的合作关系,将这些客户转变为新兴的 IBM 解决方案,例如 Watson 的人工智能。
IBM 从运行关键生产服务的大量现有客户中受益,而这些客户刚刚开始对采用云感到满意。 现有的客户群使 IBM 处于有利位置,可以在这些客户拥抱云并开始其转型之旅时为其提供协助。
像甲骨文一样,IBM 也在艰难地争取从 AWS,Azure 和 Google 获得市场份额。
亚马逊网络服务(AWS)
现在,我们将重点关注前三名云提供商。 您可能已经知道,云提供商提供的不仅仅是人工服务,还包括准系统计算和存储服务,一直到非常复杂的高级服务。 与本书中的所有其他内容一样,我们将从 AWS 开始专门研究云提供商提供的人工智能和机器学习服务。
Amazon SageMaker
Amazon SageMaker 在 2017 年在内华达州拉斯维加斯举行的 Amazon 年度 re:Invent 会议上启动。SageMaker 是一个机器学习平台,使开发人员和数据科学家可以在云中创建,训练和部署机器学习(ML)模型。 。
数据科学家在日常工作中使用的通用工具是 Jupyter 笔记本。 这些笔记本是包含计算机代码(例如 Python)和富文本元素(例如段落,方程式,图形和 URL)的组合的文档。 Jupyter 笔记本很容易为人类所理解,因为它们包含分析,描述和结果(图,图形,表格等),它们也是可以在线或在笔记本电脑上处理的可执行程序。
您可以将 Amazon SageMaker 视为 AWS 上的 Jupyter 笔记本。 与传统的 Jupyter 笔记本相比,这些是 SageMaker 的一些优势。 换句话说,这些是不同的类固醇口味:
- 像 Amazon 提供的许多机器学习服务一样,SageMaker 是一项完全托管的机器学习服务,因此您不必担心升级操作系统或安装驱动程序。
- Amazon SageMaker 提供了一些最常见的机器学习模型的实现,但是这些实现是经过高度优化的,在某些情况下,其运行速度是同一算法的其他实现的 10 倍。 此外,如果 SageMaker 没有提供开箱即用的机器学习模型,则可以引入自己的算法。
- Amazon SageMaker 可为各种工作负载提供适量的肌肉。 可以从 Amazon 提供的多种机器类型中选择可以用来训练或部署算法的机器类型。 如果您只是在尝试使用 SageMaker,则可能会决定使用
ml.t2.medium
计算机,这是可与 SageMaker 一起使用的最小计算机之一。 如果需要一些有功功率,则可以加速其计算机实例,例如ml.p3dn.24xlarge
计算机。 这样的实例所提供的功能相当于几年前被认为是超级计算机的功能,将花费数百万美元来购买。
Amazon SageMaker 使开发人员可以在整个机器学习管道中提高生产力,包括:
数据准备:Amazon SageMaker 可以与许多其他 AWS 服务无缝集成,包括 S3,RDS,DynamoDB 和 Lambda,从而使其易于提取和准备数据以供机器学习算法使用。
算法选择和训练:开箱即用,Amazon SageMaker 具有各种针对速度和准确率进行了优化的高表现,可扩展机器学习算法。 这些算法可以对 PB 级数据集执行训练,并且可以将表现提高多达类似实现的 10 倍。 这些是 SageMaker 随附的一些算法:
- BlazingText
- DeepAR 预测
- 分解机
- K 均值
- 随机剪切森林(RCF)
- 物体检测
- 图片分类
- 神经主题模型(NTM)
- IP 洞察
- K 最近邻(KNN)
- 潜在狄利克雷分布(LDA)
- 线性学习器
- Object2Vec
- 主成分分析(PCA)
- 语义分割
- 序列到序列
- XGBoost
算法调整和优化:Amazon SageMaker 提供自动模型调整,也称为超参数调整。 调整通过在指定的超参数范围内使用相同的输入数据集和相同的算法运行多个迭代来找到模型的最佳参数集。 随着训练工作的进行,计分卡将保留该模型的最佳表现版本。 “最佳”的定义基于预定义的指标。
例如,假设我们正在尝试解决二分类问题。 目标是通过训练 XGBoost 算法模型,最大化算法的曲线(AUC)度量下的面积。 我们可以为该算法调整以下超参数:
alpha
eta
min_child_weight
max_depth
为了找到这些超参数的最佳值,我们可以为超参数调整指定一个值范围。 将开始一系列训练工作,并且将根据提供最高 AUC 的版本存储最佳的超参数集。
Amazon SageMaker 的自动模型调整可以与 SageMaker 的内置算法以及自定义算法一起使用。
算法部署:在 Amazon SageMaker 中部署模型是一个两步过程:
- 创建一个端点配置,指定用于部署模型的 ML 计算实例。
- 启动一个或多个 ML 计算实例以部署模型,并公开 URI 进行调用,这将允许用户进行预测。
端点配置 API 接受 ML 实例类型和实例的初始计数。 在神经网络的情况下,配置可以包括 GPU 支持的实例的类型。 端点 API 提供了上一步中定义的基础结构。
SageMaker 部署支持一次性和批量预测。 批量预测对可以存储在 Amazon S3 或其他 AWS 存储解决方案中的数据集进行预测。
集成和调用:Amazon SageMaker 提供了多种与服务交互的方式和界面:
- Web API:Sagemaker 具有 Web API,可用于控制和调用 SageMaker 服务器实例。
- SageMaker API:与其他服务一样,Amazon 具有适用于 SageMaker 的 API,该 API 支持以下编程语言列表:
- Go
- C++
- Java
- JavaScript
- Python
- PHP
- Ruby
- Java
- Web 界面:如果您对 Jupyter 笔记本熟悉,,由于与 SageMaker 进行交互的 Web 界面是 Jupyter 笔记本,您将对 Amazon SageMaker 感到宾至如归。
- AWS CLI:AWS 命令行界面(CLI)。
Alexa,Lex 和 Polly – 会话绅士
在前面的章节中,我们讨论了 Alexa 及其在家庭中越来越普遍的存在。 现在,我们将深入研究为 Alexa 提供支持的技术,并允许您创建自己的对话机器人。
Amazon Lex 是用于建立对话智能体的服务。 Amazon Lex 和其他聊天机器人是我们这一代人的尝试通过图灵测试,我们在前面的章节中已经进行了讨论。 任何人将与 Alexa 的对话与人类对话混淆都需要一段时间。 但是,亚马逊和其他公司在使这些对话越来越自然的过程中不断取得进步。 Amazon Lex,使用与 Amazon Alexa 相同的技术,使开发人员可以快速构建复杂的自然语言,会话智能体或“聊天机器人”。 对于简单的情况,无需任何编程就可以构建其中的一些聊天机器人。 但是,可以使用 AWS Lambda 作为集成技术将 Lex 与 AWS 栈中的其他服务集成。
稍后,我们将整整一章专门介绍如何创建聊天机器人,因此我们将在本节中简短介绍。
Amazon Comprehend – 自然语言处理
Amazon Comprehend 是 AWS 提供的自然语言处理(NLP)服务。 它使用机器学习来分析内容,执行实体识别以及发现隐式和显式关系。 公司开始意识到他们每天产生的大量数据中都有有价值的信息。 可以从客户的电子邮件,支持通知单,产品评论,呼叫中心对话和社交媒体互动中确定有价值的见解。 直到最近,尝试获得这些见解都在成本上处于禁止状态,但是 Amazon Comprehend 之类的工具使对大量数据进行分析具有成本效益。
该服务的另一个优点是,它是另一项完全受管的 AWS 服务,因此无需置备服务器,安装驱动程序和升级软件。 它使用简单,不需要 NLP 的丰富经验即可快速提高生产力。
与其他 AWS AI/ML 服务一样,Amazon Comprehend 与其他 AWS 服务(例如 AWS Lambda 和 AWS Glue)集成。
用例:Amazon Comprehend 可用于扫描文档和识别这些文档中的模式。 此功能可以应用于一系列用例,例如情感分析,实体提取和按主题组织文档。
例如,Amazon Comprehend 可以分析来自与客户的社交媒体互动中的文本,识别关键短语,并确定客户的体验是正面还是负面。
控制台访问:可以从 AWS 管理控制台访问 Amazon Comprehend 。 将数据提取到服务中的最简单方法之一是使用 Amazon S3。 然后,我们可以调用 Comprehend 服务以分析文本中的关键短语和关系。 理解可以为每个用户请求返回一个置信度分数,以确定准确率的置信度; 百分比越高,服务越有信心。 Comprehend 可以轻松地批量处理单个请求或多个请求。
可用的应用编程接口(API):截至为止,Comprehend 提供了六个不同的 API 来提供见解。 他们是:
- 关键字提取 API:标识关键字和术语。
- 情感分析 API:返回文本的整体含义和感觉,无论是是肯定,否定,中立还是混合。
- 语法 API:允许用户标记化文本以定义单词边界,并在其不同词性(例如名词和动词)中标记单词。
- 实体识别 API:标识并标记文本中的不同实体,例如人物,地点和公司。
- 语言检测 API:标识用于编写文本的主要语言。 服务可以识别一百多种语言。
- 自定义分类 API:使用户能够构建自定义文本分类模型。
行业特定的服务:Amazon Comprehend Medical 于 2018 年在 AWS re:Invent 上发布。它专为医疗行业构建,可以识别行业特定的术语。 Comprehend 还提供了特定的医学命名实体和关系提取 API。 AWS 不会存储或使用 Amazon Comprehend Medical 的任何文本输入来进行未来的机器学习训练。
Amazon Rekognition – 图片和视频
不,不是错字。 亚马逊用 k 而不是 c 命名其识别服务。 Amazon Rekognition 可以执行图像和视频分析,并使用户可以将此功能添加到其应用中。 Amazon Rekognition 已经接受了数百万张带有标签的图像的预训练。 因此,该服务可以快速识别:
- 对象类型:椅子,桌子,汽车等
- 名人:演员,政客,运动员等
- 人员:人脸分析,人脸表情,人脸质量,用户验证等
- 文本:将图像识别为文本并将其转换为文本
- 场景:跳舞,庆祝,吃饭等
- 不当内容:成人,暴力或视觉干扰的内容
Amazon Rekognition 已经识别出数十亿张图像和视频,并使用它们不断变得越来越好。 深度学习在图像识别领域的应用可以说是过去几年中最成功的机器学习应用,而 Amazon Rekognition 利用深度学习来提供令人印象深刻的结果。 要使用它,不需要具有高水平的机器学习专业知识。 Amazon Rekognition 提供了一个简单的 API。 要使用它,将图像和一些参数一起传递到服务,就是这样。 Amazon Rekognition 只会继续变得更好。 它使用得越多,收到的输入就越多,并且从这些输入中学到的越多。 此外,Amazon 继续增强服务并向该服务添加新功能。
Amazon Rekognition 最受欢迎的一些用例和应用包括:
对象,场景和活动检测:使用 Amazon Rekognition,您可以识别成千上万种不同类型的对象(例如,汽车,房屋,椅子等)和场景(例如, 城市,购物中心,海滩等)。 分析视频时,可以识别帧中正在发生的特定活动,例如“清空后备箱”或“孩子们玩耍”。
性别识别:Amazon Rekognition 可用于进行有根据的猜测,以确定图像中的人是男性还是女性。 该功能不应用作一个人的性别的唯一决定因素。 它并不意味着以这种方式使用。 例如,如果男演员戴着长发假发和耳环饰演角色,则可能被识别为女性。
人脸识别和分析:人脸识别系统的用途之一是从图像或视频中识别和验证人。 这项技术已经存在了几十年,但是直到最近,它的应用才变得更加流行,便宜和可用,这在很大程度上要归功于深度学习技术和 Rekognition 等服务的普遍存在。 人脸识别技术支持当今的许多应用,例如照片共享和存储服务,并且是智能手机身份验证工作流中的第二个因素。
一旦我们认识到物体是一张脸,就可能要执行进一步的人脸分析。 Amazon Rekognition 可以帮助确定的一些属性包括:
- 睁眼或闭眼
- 心情:
- 快乐
- 伤心
- 愤怒
- 惊讶
- 恶心
- 冷静
- 困惑
- 恐惧
- 发色
- 眼睛的颜色
- 胡须
- 眼镜
- 年龄范围
- 性别
- 人脸的视觉几何
当需要在几秒钟内搜索并组织数百万个图像,生成诸如人的情感之类的元数据标签或识别一个人时,这些检测到的属性很有用。
路径:可以使用视频文件通过 Amazon Rekognition 在场景中跟踪人的路径。 例如,如果我们看到一个图像,其中包含一个人,他的行李箱周围有行李箱,那么我们可能不知道该人是否正在将行李箱从行李箱中取出并到达,或者他们是否正在将行李箱放入行李箱中并离开。 通过使用路径分析视频,我们将能够做出此确定。
不安全的内容检测:亚马逊 Rekognition 可以帮助识别图像和视频内容中潜在的不安全或不适当的内容,并且可以提供详细的标签,以根据先前确定的标准准确控制对这些资产的访问。
名人识别:可以在图像和视频库中快速识别名人和名人,以将照片和镜头分类。 此功能可用于市场营销,广告和媒体行业用例。
图像中的文本:一旦我们识别出图像中包含文本,就自然要将该图像中的字母和单词转换为文本。 例如,如果 Rekognition 不仅能够识别物体是车牌,而且还可以将图像转换为文本,则可以很容易地根据机动车部门的记录对其进行索引,并跟踪个人及其下落。
亚马逊翻译
Amazon Translate 是另一项 Amazon 服务,可用于将以一种语言编写的大量文本翻译成另一种语言。 Amazon Translate 是按使用付费的,因此仅在您提交需要翻译的内容时才需要付费。 作为 2019 年 10 月的,Amazon Translate 支持 32 种语言:
语言 | 语言代码 |
阿拉伯 | ar |
简体中文 | zh |
繁体中文 | zh-TW |
捷克文 | cs |
丹麦文 | da |
荷兰语 | nl |
英语 | en |
芬兰 | fi |
法文 | fr |
德语 | de |
希腊语 | el |
希伯来语 | he |
印地语 | hi |
匈牙利 | hu |
印度尼西亚 | id |
意大利文 | it |
日本 | ja |
韩语 | ko |
马来语 | ms |
挪威 | no |
波斯语 | fa |
波兰语 | pl |
葡萄牙语 | pt |
罗马尼亚语 | ro |
俄语 | ru |
西班牙文 | es |
瑞典 | sv |
泰国 | th |
土耳其 | tr |
乌克兰 | uk |
乌尔都语 | ur |
越南文 | vi |
除少数例外,大多数这些语言都可以从一种翻译为另一种。 用户还可以向字典中添加项目以自定义术语,并包括特定于其组织或用例的术语,例如品牌和产品名称。
Amazon Translate 使用机器学习和连续学习模型来改善其翻译的表现。
可以通过三种不同的方式访问该服务,就像可以访问许多 AWS 服务一样:
- 在 AWS 控制台中,翻译少量文本片段并对该服务进行采样。
- 使用 AWS API(支持的语言为 C++ ,Go,Java,JavaScript,.NET,Node.js,PHP,Python 和 Ruby)。
- 可以通过 AWS CLI 访问 Amazon Translate。
用于 Amazon Translate
许多公司将 Amazon Translate 与其他外部服务一起使用。 此外,Amazon Translate 可以与其他 AWS 服务集成。 例如,翻译可与 Amazon Comprehend 结合使用,以从社交媒体源中提取预定的实体,情感或关键字,然后翻译提取的术语。 在另一个示例中,该服务可以与 Amazon S3 配对以翻译文档存储库并使用 Amazon Polly 讲翻译语言。
但是,使用 Amazon Translate 并不意味着人工翻译不再起作用。 一些公司将 Amazon Translate 与人工翻译配对,以提高翻译过程的速度。
亚马逊机器学习
在出现 Amazon SageMaker 之前,就有了 Amazon Machine Learning 或 Amazon ML。 Amazon ML 是一项更简单的服务,在某些情况下仍可以是功能强大的工具。 Amazon ML 最初于 2015 年 4 月在旧金山的 AWS 峰会上发布。 Amazon ML 使所有技能水平的开发人员都可以轻松使用机器学习技术。 Amazon ML 提供了可视化工具和向导,可以指导用户完成创建机器学习模型的过程,而无需学习复杂的 ML 算法和技术。 一旦模型准备就绪,Amazon ML 即可轻松获得预测。 应用可以使用简单的 API,而不必在完全托管的服务中实现自定义预测代码。
Amazon Transcribe – 转录
在 2017 年 re:Invent 会议上发布的另一项服务是 Amazon Transcribe。 您可以将 Amazon Transcribe 当作您的私人秘书,在讲话时做笔记。
Amazon Transcribe 是自动语音识别(ASR)服务,允许开发人员向各种应用添加语音到文本功能。 Amazon Transcribe API 可用于分析存储的音频文件。 该服务返回一个包含转录语音的文本文件。 Amazon Transcribe 也可以实时使用。 它可以接收实时音频流,并将生成包含转录文本的实时流。
Amazon Transcribe 可用于转录客户服务呼叫并生成音频和视频内容的字幕。 该服务支持常见的音频格式,例如 WAV 和 MP3。 它可以为每个单词生成一个时间戳。 这有助于使用生成的文本快速找到原始音频源。 像其他 Amazon 机器学习服务一样,Amazon Transcribe 不断从文本中学习其正在处理以不断改进服务。
Amazon Textract – 文档分析
机器学习中最困难的问题之一是识别笔迹。 每个人的笔迹都不一样,我们中有些人的笔迹很糟糕,有时甚至在写完几分钟后我们还是听不懂。 不可以,Amazon 尚未掌握解密鸡抓痕的方法,但是 Amazon Textract 是一项服务,可以将包含文本的图像转换为等效的文本。 如果我们能够扫描那些文档,将它们转换为文本,对其进行索引,并使用户能够搜索这些文档的内容,那么有很多扫描和传真的文档都放在抽屉里,可以为所有者带来很多价值。
Amazon Textract 使用户可以从文档,表单和表格中提取文本。 Amazon Textract 可以自动检测文档和关键页面元素的布局。 它可以识别嵌入形式或表中的数据,并在页面上下文中提取该数据。 然后,该信息可以与其他 AWS 服务集成,并用作 AWS Lambda 调用的输入或用作 Amazon Kinesis 的流。
Microsoft Azure
在介绍了 AWS 之后,让我们看一下 Microsoft 在云服务领域提供的功能:Microsoft Azure。
Microsoft Azure 机器学习工作室
Microsoft Azure Machine Learning Studio 是 Microsoft 对 Amazon SageMaker 的回答。 Machine Learning Studio 是一个协作工具,具有简单的拖放界面,允许用户构建,测试和部署机器学习模型。 Machine Learning Studio 支持模型发布,该模型发布可被其他应用使用,并可轻松与 BI 工具(例如 Excel)集成。
Machine Learning Studio 交互式工作区:在第 3 章,“机器学习管道”中,我们学习了关于机器学习管道的信息。 Machine Learning Studio 交互式工作区通过允许用户轻松地将数据提取到工作区,转换数据,然后通过各种数据操作和统计功能分析数据并最终生成预测,从而简化了管道开发。 开发机器学习管道通常是一个迭代过程,而工作区使执行此迭代开发变得简单。 在修改各种功能及其参数时,您将能够可视化和分析模型的表现,直到对结果满意为止。
Azure Machine Learning Studio 提供了一个交互式的可视化工作区,可以轻松地构建,测试和迭代预测分析模型。 要将数据集带入工作区,可以将其拖放。 您还可以将分析模块拖到交互式画布上,并将它们连接在一起以形成初始实验,然后可以在 Machine Learning Studio 中运行。 如果结果不令人满意,可以修改实验参数并一次又一次地运行直到结果令人满意。 一旦表现令人满意,就可以将训练实验转换为预测性实验,并且可以将其发布为网络服务,以便用户和其他服务可以访问该模型 。
Learning Studio 不需要任何编程。 通过直观地连接数据集和模块以构建预测分析模型来构建实验。
Machine Learning Studio 入门–要开始使用,您可以使用 Azure 创建免费层帐户。 在撰写本文时,免费帐户的好处是:
- 12 个月的免费产品,例如虚拟机,存储和数据库
- 不符合免费套餐资格的服务可获得$ 200 的赠送金额
- 除非您专门升级到付费帐户,否则不会自动收费
- 此外,Azure 有超过 25 种始终免费的产品,包括无服务器产品和 AI 服务
创建帐户后,您可以访问 Azure Machine Learning Studio。
登录后,您会在左侧看到以下标签:
- 项目:项目是实验,数据集,笔记本和其他资源的集合
- 实验:可以创建,编辑,运行和保存实验
- Web 服务:实验可以作为 Web 服务进行部署和公开
- 笔记本:Studio 还支持 Jupyter 笔记本电脑
- 数据集:已上传到 Studio 的数据集
- 训练模型:经过训练并保存在实验中的模型
- 设置:设置可用于配置帐户和资源。
Azure 机器学习库:该库是数据科学社区可以共享以前使用 Cortana Intelligence Suite 中的组件创建的解决方案的地方。
实验的组成部分:实验由数据集和分析模块组成,可以将其连接以构建预测分析模型。 有效的实验具有以下特征:
- 实验至少有一个数据集和一个模块
- 数据集只能连接到模块
- 模块可以连接到数据集或其他模块
- 模块的所有输入端口必须与数据流有某些连接
- 必须设置每个模块的所有必需参数
可以从头开始创建实验,也可以使用现有实验作为模板来创建实验。
数据集:数据集是已上传到 Machine Learning Studio 的数据,因此可以在实验中使用。 Machine Learning Studio 包含几个示例数据集,并且可以根据需要上载更多数据集。
模块:模块是可以对数据执行的算法。 Machine Learning Studio 具有各种模块,包括:
- 数据提取过程
- 训练函数
- 评分函数
- 验证过程
更具体的示例:
- ARFF 转换模块:将 .NET 序列化的数据集转换为属性关系文件格式(ARFF)
- 计算基本统计模块:计算基本统计数据,例如均值,标准差等
- 线性回归模型:创建基于在线梯度下降的线性回归模型
- 评分模型:为训练有素的分类或回归模型评分
模块可能具有一组参数,可用于配置模块的内部算法。
模型部署:预测分析模型准备就绪后,您可以直接从 Machine Learning Studio 将其部署为 Web 服务。
Azure 机器学习服务
Azure 机器学习(AML)服务是一个平台,可让数据科学家和数据工程师大规模地在云中训练,部署,自动化和管理机器学习模型 。 服务的用户可以使用基于 Python 的库来创建功能强大的应用和工作流。 AML 服务是一个框架,允许开发人员使用预定义的数据集训练模型,然后将其模型作为 Web 服务包装在 Docker 容器中,并使用各种容器协调器进行部署。
可以通过以下两种方式之一访问和使用 Azure 机器学习服务:
- 通过软件开发工具包(SDK)
- 使用服务可视界面
如果您认为这听起来很像 Azure Machine Learning Studio,那么您会认为是正确的。 这些是类似的服务,并且在某个时候,Microsoft 可能会决定将它们合并在一起或弃用其中之一。 如果它们不推荐使用其中之一,则可以高度肯定地假定 Microsoft 将提供一种将其中一项服务中开发的工作流和应用迁移到另一项中的方法。
机器学习 Studio 与 Azure 机器学习服务有何不同? 这些是主要区别,这使您可以决定使用哪个:
Azure 机器学习服务 | Azure 机器学习工作室 |
训练和评分模型的混合部署。 可以在本地训练模型并将其部署在云上,反之亦然。 | 非常适合初学者 |
自由使用不同的框架和机器实例类型 | 可以快速创建标准实验,但更难以自定义 |
支持自动 ML 和自动超参数调整 | 全面托管的服务 |
在本地 |
在这里,我们提供一个图表,突出显示每种服务所支持的差异和各种功能:
功能 | Azure 机器学习工作室 | Azure 机器学习服务 SDK | Azure 机器学习服务可视界面 |
发行年份 | 2015 | 2018 | 2019(预览) |
用户界面 | 基于网络 | 基于 API | 基于网络 |
云端支持 | 是 | 是 | 是 |
本地 | 否 | 是 | 否 |
工具支持 | 基于网络 | 视觉工作室、Azure 笔记本、Python 接口 | 基于网络 |
支持 GPU | 否 | 是 | 是 |
内置算法 | 分类、回归、聚类、时间序列、文本分析、异常检测 | 外部包可以导入 | 分类、回归、聚类 |
自动超参数调整 | 否 | 是 | 否 |
自动 ML | 否 | 是 | 否 |
易于扩展 | 不简单 | 可以通过 PIP 轻松安装 Python 包 | 不简单 |
Python 支持 | 是 | 是 | 是 |
R 支持 | 是 | 否 | 否 |
内置容器 | 否 | 是 | 否 |
Azure 认知服务
决策服务:允许用户构建可提供建议并支持有效决策的应用。
视觉服务:启用应用,这些应用可以识别,标识,字幕,索引以及适度的图像和视频。
语音服务:此服务将语音转换为文本,并将文本转换为自然声音。 它还可以执行从一种语言到另一种语言的翻译。 此外,它还支持说话人验证和识别。
搜索服务:Bing 可以向应用添加搜索支持,并使用户可以通过单个 API 调用来搜索数十亿个网页,图像,视频和新闻文章。
语言服务:使应用能够使用预先构建的脚本处理自然语言,以评估文本情感并确定文本的整体实体。
Google Cloud Platform(GCP)
看了 Microsoft Azure 提供的服务之后,让我们继续讨论另一个替代的云平台:GCP。 首先,我们将讨论 GCP 的 AI Hub 服务。
AI Hub
AI Hub 是 Google Cloud Platform 中可用的服务之一。 AI Hub 是即插即用 AI 组件的完全托管库,可用于创建端到端机器学习管道。 AI Hub 提供了多种现成的机器学习算法。 AI Hub 提供企业级协作功能,使公司可以私下托管其机器学习工作流程并促进重用和共享。 您还可以轻松地将模型部署到 Google Cloud 以及其他环境和云提供商的生产环境中。 AI Hub 于 2018 年发布,目前尚处于早期阶段。 考虑到 Google 对 AI 研究的重视,我们希望 AI Hub 能够迅速成熟并继续以更快的速度提供更多功能。
组件和代码发现:AI Hub 是一个内容存储库,可让用户快速发现高质量的内容。 可通过 AI Hub 访问的一些发布者是:
- Google AI
- Google Cloud AI
- Google Cloud 合作伙伴
如果在企业内使用集线器,则用户还可以找到公司内其他团队构建的其他组件。
协作:AI 集线器提高了用户生产力,并使他们避免了重复劳动。 AI Hub 提供了高度精细的控件,以仅与组织中应该有权访问组件的用户共享组件。 它还使用户可以访问由 Google 工程师和研究人员创建的预定义机器学习算法,以及 Azure 合作伙伴和其他发布者共享的其他代码。
部署:AI Hub 可以针对特定业务需求修改和定制算法和管道。 它还提供了用于部署经过训练的模型的直观机制。 这些模型可以部署在 Google Cloud 或其他环境和云提供商中。
Google Cloud AI 组成部分
除了 AI Hub 的(可以与 Amazon SageMaker 和 Azure 机器学习 Studio 相比)之外,Google Cloud 在完全托管服务方面还提供了与 AWS 和 Azure 类似的产品,这些服务简化了机器学习在文本中的应用, 语言,图像和视频。 Google 在 Google Cloud AI 构件框架下组织了许多此类托管服务。 对于许多此类托管服务,有两种与之交互的方式– AutoML 和 API。 AutoML 用于自定义模型,API 用于预训练模型。 AutoML 和 API 可以单独使用,也可以一起使用。
Google Cloud AutoML 自定义模型:AutoML 服务使用 Google 的最新迁移学习和神经架构搜索技术,允许用户为各种用例创建特定于领域的自定义模型。
Google Cloud 预训练的 API:在处理常见用例时,使用预训练的 API 的 Google 服务用户可以立即变得富有成效,而无需事先训练模型。 预训练的 API 不断透明地升级,以提高这些模型的速度和准确率。
Vision AI 和 AutoML Vision:该服务允许用户使用 AutoML Vision 或使用预训练的 Vision API 模型从图像中获取见解。 此服务可以检测情感,理解文字等。
要使用该服务,可以使用自定义图像模型上传和分析图像。 该服务具有易于使用的可视界面。 该服务使您可以优化模型的准确率,延迟和大小。 结果可以导出到云中的其他应用或边缘的一系列设备。
Google Cloud 的 Vision API 提供了功能强大的经过预训练的机器学习模型,可以使用 RESTful 和 RPC API 调用进行访问。 该服务可以快速标记图像并对其进行分类。 该服务已经过预训练,已经包含数百万个类别。 它也可以用于人脸识别和分析,以及识别图像中的标题并将其转换为文本。
AutoML 视频智能和视频智能 API:AutoML 视频智能服务具有一个简单的界面,该界面可以使用自定义模型识别,跟踪和分类视频中的对象。 该服务不需要编程或人工智能方面的广泛背景。 该服务用于需要自定义标签的应用,而这些标签不能由经过预训练的 Video Intelligence API 生成。
Video Intelligence API 具有经过预训练的模型,可以识别各种常见的对象,场景和活动。 除了存储的视频外,它还支持流视频。 随着处理更多图像,它会随着时间的推移自动透明地改善。
AutoML 翻译和翻译 API:很少或没有编程经验的开发人员和翻译人员都可以创建生产质量的模型。 Translation API 使用预训练的神经网络算法来提供世界一流的机器翻译,在某些情况下,这种翻译已开始与人类水平的表现相抗衡。
AutoML 自然语言和自然语言 API:该服务可用于对文本分类,执行实体提取和情感检测,所有这些都使用简单易用的 API。 用户可以利用 AutoML 自然语言界面来提供数据集并确定将使用哪些自定义模型。
Natural 语言 API 具有预训练的模型,该模型使 API 的用户可以访问自然语言理解(NLU)功能,包括:
- 实体分析
- 情感分析
- 内容分类
- 实体情感分析
- 语法分析
Dialogflow:Dialogflow 是开发服务,允许用户创建对话智能体。 它可以用来构建聊天机器人,以实现自然而丰富的交互。 它允许服务的用户一次开发智能体,然后将它们部署到各种平台,包括:
- 谷歌助手
- Facebook Messenger
- Slack
- Alexa 语音服务
文字转语音:Google Cloud 文字转语音可以将文字转换为类似人类的语音,并具有 30 多种语言和口音的 180 多种语音。 例如,它可以模仿美国的口音或英国的口音。 它使用语音合成(WaveNet)和 Google 开发的神经网络来提供高保真音频。 用户可以调用 API 并创建逼真的交互。 不难想象,我们很快就会看到这种技术已嵌入到各种客户服务应用中。
语音转文本:您可以将视为该服务与先前的服务相反。 如果文本到语音是声音,则语音到文本提供了耳朵。 Google Cloud 语音转文本功能使服务的用户可以利用神经网络模型将音频文件转换为文本。 这些模型的复杂性对服务的用户完全隐藏了,他们可以调用一个易于使用的 API 来调用它。 撰写本文时,API 支持 120 多种语言和变体。 它可以用于:
- 在应用中启用语音命令
- 转录呼叫中心对话
- 与工作流程中的其他 Google 和非 Google 服务集成
- 实时处理音频以及预先录制的版本
AutoML 表:该服务使分析人员,开发人员和数据科学家可以在结构化数据上构建和部署机器学习模型。 在许多用例中,它几乎不需要编码,因此可以大大提高部署速度。 在这些情况下,可通过类似向导的界面进行配置。 当需要编码时,AutoML Tables 支持 Colab 笔记本。 这些笔记本是功能强大的笔记本,类似于 Jupyter 笔记本,并具有许多使其易于使用并与其他用户协作的附加功能。 该服务是域无关的,因此可以用来解决各种各样的问题。 截至 2019 年 10 月,该服务仍未普遍可用,但可以通过 Beta 版访问。
推荐 AI:此 Google 服务可以大规模提供高度个性化的产品推荐。 二十多年来,Google 一直在其旗舰产品(例如 Google Ads,Google 搜索和 YouTube)中提供建议。 建议 AI 利用该经验,使服务的用户能够在各种应用和用例中提供个性化的建议,以满足个人客户的需求和偏好。 在撰写本文时,该产品也处于 beta 版本,因此通常不可用。
总结
在本章中,我们看到所有主要的技术公司都参与了高风险的军备竞赛,成为云计算的领军人物。 在计算的历史中,随着不同技术的出现,最常见的结果是让一个玩家主导整个空间,而其他所有竞争者都被放任其职。 云可能是计算历史上出现的最重要的技术。 当客户决定他们首选的云提供商是谁时,即使他们现在可能尚未意识到,他们正在做出决定,将他们锁定在该云提供商的生态系统中,并且很难从中摆脱出来并跳到另一个云提供商。
云供应商意识到了这一点的重要性,并争相与竞争对手的能力相提并论。 当我们分析来自前三名云供应商的机器学习产品时,我们在本章中清楚地看到了这一点。 他们正在努力相互区分,同时在每种服务和功能上都力求彼此匹敌。 在未来几年内,这些云产品将如何发展以及这些供应商将提供哪些伟大的新服务,尤其是在人工智能和机器学习领域,将是令人兴奋的。
作为技术专家,对我们而言,一个缺点是很难跟上所有有趣的玩具和技术,但是如果没有别的,那么探索它们就应该是激动人心的旅程。
谈到玩具,在下一章中,我们将探讨如何使用人工智能构建游戏,并将我们学到的一些概念加以利用。
13 使用人工智能构建游戏
在本章中,我们将学习如何使用称为组合搜索的人工智能技术来构建游戏。 在其最基本的形式中,可以将其视为暴力方法。 我们探索每种可能的解决方案。 在本章的后面,我们将变得更加聪明,找到一种使搜索短路的方法,而不必尝试所有可能的方法。 我们将学习如何使用搜索算法有效地提出赢得一系列游戏的策略。 然后,我们将使用这些算法为不同的游戏构建智能机器人。
在本章结束时,您将对以下概念有更好的理解:
- 游戏中的搜索算法
- 组合搜索
- Minimax 算法
- Alpha-Beta 修剪
- Negamax 算法
- 构建一个机器人来玩 Last Coin Stand
- 构建一个玩井字棋的机器人
- 构建两个机器人来相互玩 Connect4
- 构建两个机器人来对抗 Hexapawn
在游戏中使用搜索算法
搜索算法通常在游戏中用于确定策略的。 该算法搜索可能的游戏动作并选择最佳的动作。 实现这些搜索时需要考虑各种参数-速度,准确率,复杂性等。 这些算法考虑了给定当前游戏状态的所有可能的游戏动作,然后评估每个可能的动作以确定最佳动作。 这些算法的目标是找到最终导致游戏获胜的最佳动作。 而且,每个游戏都有不同的规则和约束集。 这些算法在探索最佳动作时会考虑这些规则和约束。
没有对手的游戏比拥有对手的游戏更容易优化。 具有多个玩家的游戏,游戏玩法变得更加复杂。 让我们考虑一个两人游戏。 玩家为赢得比赛而进行的每一个举动,对方玩家都会采取行动以阻止该玩家获胜。 因此,当搜索算法从当前状态中找到最佳移动方式集时,它就不能不考虑对方玩家的反向移动而仅仅进行移动。 这意味着每次移动后都需要不断重新评估搜索算法。
让我们讨论一下计算机如何感知任何给定的游戏。 我们可以将游戏视为搜索树。 该树中的每个节点代表一个未来状态。 例如,如果您正在玩井字棋(圆圈和叉),则可以构造一棵树来表示所有可能的移动。 我们从树的根开始,这是游戏的起点。 该节点将具有几个代表各种可能动作的子代。 反过来,在对手进行更多移动之后,这些子项将拥有更多代表游戏状态的子项。 树的终端节点代表游戏的最终动作。 游戏将以平局结束,或者其中一名玩家将赢得比赛。搜索算法搜索该树以在游戏的每个步骤做出决策。 现在,我们将学习各种搜索技术,包括如何进行详尽的组合搜索,以帮助我们在井字棋中永不丢失,并解决许多其他问题。
组合搜索
搜索算法似乎可以解决为游戏添加智能的问题,但是存在一个缺点。 这些算法采用一种称为穷举搜索的搜索类型,也称为暴力搜索。 它基本上探索了整个搜索空间并测试了每种可能的解决方案。 这意味着该算法将必须先探索所有可能的解决方案,然后才能获得最佳解决方案。
随着游戏变得越来越复杂,暴力搜索可能不是最好的方法,因为可能性越来越多。 搜索很快变得难以处理。 为了解决该问题,可以使用组合搜索来解决问题。 组合搜索是指一个研究领域,其中搜索算法使用启发式方法有效地探索解决方案空间,以减小搜索空间的大小。 这在象棋或围棋之类的游戏中很有用。
组合搜索通过使用修剪策略有效地工作。 这些策略通过消除显然错误解决方案来避免测试所有可能的解决方案。 这有助于节省时间和精力。 现在,我们已经了解了详尽的组合搜索及其局限性,我们将开始探索捷径,“修剪”搜索树并避免测试每个组合的方法。 在以下各节中,我们将探索一些特定的算法,这些算法使我们能够执行组合搜索。
Minimax 算法
现在,我们已经简要地讨论了组合搜索,下面我们来讨论组合搜索算法所采用的启发式方法。 这些启发式方法可用于加快搜索策略,而 Minimax 算法就是组合搜索使用的此类策略之一。 当两个玩家互相对抗时,他们的目标是截然相反的。 每个玩家都试图赢得胜利。 因此,每一方都需要预测对方选手要做什么才能赢得比赛。 牢记这一点,Minimax 试图通过战略来实现这一目标。 它将尝试最小化对手试图最大化的函数。
如前所述,暴力仅在具有少量可能动作的简单游戏中起作用。 在更复杂的情况下,计算机无法通过所有可能的状态来找到最佳游戏玩法。 在这种情况下,计算机可以尝试使用启发式方法基于当前状态计算最佳移动。 计算机构造一棵树,它从底部开始。 它评估哪些举动会对自己的对手有利。 该算法基于以下前提:对手将做出哪些动作,前提是对手将做出最有利于他们的动作,从而使计算机受益最少。 此结果是树的终端节点之一,计算机使用此位置向后工作。 可以为计算机可用的每个选项分配一个值,然后可以选择最高的值来执行操作。
Alpha-Beta 修剪
Minimax 搜索是一种有效的策略,但最终仍会探索树上不相关的部分。 当在节点上找到指示符,表明该子树中不存在解决方案时,则无需评估该子树。 但是 Minimax 搜索过于保守,因此最终探索了其中的一些子树。
Alpha-Beta 算法更智能,可以避免搜索发现的树中没有解决方案的部分。 此过程称为,称为修剪,Alpha-Beta 修剪是一种策略,用于避免搜索树中不包含解决方案的部分。
Alpha-Beta 修剪中的 Alpha 和 Beta 参数是指计算过程中使用的两个边界。 这些参数是指限制可能的解决方案集的值。 这是基于已经探索过的树的部分的。 Alpha 是可能解的最大上限,而 Beta 是可能解的最小上限。
如前所述,可以根据当前状态为每个节点分配一个值。 当算法将任何新节点视为解决方案的潜在路径时,如果该节点值的当前估计值介于 Alpha 和 Beta 之间,则可以进行计算。 这是的修剪方式。
Negamax 算法
Negamax 算法是 Minimax 的一种变体,在现实世界的实现中经常使用。 两人游戏通常是零和游戏,这意味着一个玩家的损失等于另一个玩家的收益,反之亦然。 Negamax 广泛使用此属性来提出增加其赢得游戏机会的策略。
就游戏而言,给第一位玩家的给定位置的值是给第二位玩家的值的相反数。 每个玩家都在寻找能够最大程度地伤害对手的举动。 此举所产生的值应使对手获得最小值。 这两种方法都可以无缝地工作,这意味着可以使用一种方法来评估位置。 就简单性而言,这是它比 Minimax 更具优势的地方。 Minimax 要求第一个玩家选择具有最大值的移动,而第二个玩家必须选择具有最小值的移动。 这里也使用 Alpha-Beta 修剪。 现在,我们已经研究了几种最流行的组合搜索算法,下面我们安装一个库,以便我们可以构建一些 AI 并查看这些算法的实际应用。
安装easyAI
库
在本章中,我们将使用名为easyAI
的库。 这是一个人工智能框架,它提供了构建两人游戏所需的所有功能。 您可以在此处了解的更多信息。
通过运行以下命令进行安装:
$ pip3 install easyAI
为了使用某些预建例程,需要访问某些文件。 为了易于使用,本书随附的代码包含一个名为easyAI
的文件夹。 确保将此文件夹放置在与代码文件相同的文件夹中。 此文件夹基本上是easyAI
GitHub 存储库的子集,可在此处找到。
您可以遍历源代码以使自己更加熟悉。
建立一个玩 Last Coin Standing 的机器人
在此游戏中,有一堆硬币,每个玩家轮流从该堆中取出许多硬币。 可从堆中取出的硬币数量有上限和下限。 游戏的目的是避免拿最后一枚硬币。 该秘籍是easyAI
库中给出的“骨头游戏”秘籍的一种变体。 让我们看看如何构建一个可以与用户对战的游戏。
创建一个新的 Python 文件,并导入以下包:
from easyAI import TwoPlayersGame, id_solve, Human_Player, AI_Player from easyAI.AI import TT
创建一个类来处理游戏的所有操作。 该代码继承自easyAI
库中提供的基类TwoPlayersGame
。 为了使代码正常运行,必须定义几个参数。 第一个是players
变量。 稍后将讨论player
对象。 使用以下代码创建类:
class LastCoinStanding(TwoPlayersGame): def __init__(self, players): # Define the players. Necessary parameter. self.players = players
定义要开始游戏的玩家。 选手从一开始编号。 因此,在这种情况下,玩家一开始游戏:
# Define who starts the game. Necessary parameter. self.nplayer = 1
定义堆中的硬币数量。 您可以在这里自由选择任何数字。 在这种情况下,让我们选择25
:
# Overall number of coins in the pile self.num_coins = 25
定义任何动作中可以取出的最大硬币数。 您也可以自由选择此参数的任何数字。 在我们的情况下,我们选择4
:
# Define max number of coins per move self.max_coins = 4
定义所有可能的动作。 在这种情况下,玩家每一步可以拿 1、2、3 或 4 个硬币:
# Define possible moves def possible_moves(self): return [str(x) for x in range(1, self.max_coins + 1)]
定义一种方法移除硬币并跟踪堆中剩余的硬币数量:
# Remove coins def make_move(self, move): self.num_coins -= int(move)
通过检查剩余的硬币数量来检查是否有人赢得了比赛:
# Did the opponent take the last coin? def win(self): return self.num_coins <= 0
在有人赢得比赛后停止游戏:
# Stop the game when somebody wins def is_over(self): return self.win()
根据win
方法计算分数。 有必要定义此方法:
# Compute score def scoring(self): return 100 if self.win() else 0
定义一种方法来显示桩的当前状态:
# Show number of coins remaining in the pile def show(self): print(self.num_coins, 'coins left in the pile')
定义main
函数并从定义转置表开始。 换位表用于游戏中以存储位置和运动,以加快算法的速度。
输入以下代码:
if __name__ == "__main__": # Define the transposition table tt = TT()
定义方法ttentry
以获取硬币数。 这是一个可选方法,用于创建用于描述游戏的字符串:
# Define the method LastCoinStanding.ttentry = lambda self: self.num_coins
让我们使用 AI 解决游戏。 函数id_solve
用于使用迭代加深来解决给定的游戏。 它基本上确定了谁可以使用所有途径赢得比赛。 它看起来可以回答以下问题:
- 第一个玩家能否通过完美玩法来赢得胜利?
- 电脑会永远输给一个完美的对手吗?
方法id_solve
多次探索了游戏的 Negamax 算法中的各种选项。 它总是从游戏的初始状态开始,并且需要不断增加深度才能继续进行。 它将一直这样做,直到分数表明有人将赢或输。 中的第二个参数采用该方法尝试使用的深度列表。 在这种情况下,它将尝试从2
到20
的所有值:
# Solve the game result, depth, move = id_solve(LastCoinStanding, range(2, 20), win_score=100, tt=tt) print(result, depth, move)
在计算机上启动游戏:
# Start the game game = LastCoinStanding([AI_Player(tt), Human_Player()]) game.play()
完整代码在文件coins.py
中给出。 这是一个交互式程序,因此它将期望用户输入。 如果您运行代码,则基本上可以与计算机对抗。 您的目标是迫使计算机拿走最后的硬币,以便您赢得比赛。 如果运行代码,最初将获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0kq1K82-1681568737507)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_01.png)]
图 1:最后一次站立硬币游戏的初始输出
如果向下滚动,将在结尾处看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7BNBi1DA-1681568737507)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_02.png)]
图 2:最后一枚硬币站立式游戏的最终输出
如我们所见,计算机赢得了游戏,因为用户拿起了最后一个硬币。
让我们来看看为另一个游戏[tf-tac-toe]构建机器人。
建立一个玩井字棋的机器人
井字棋(Nights and Crosss)是,也许是世界上最著名的游戏之一。 让我们看看如何构建一个可以与用户对战的游戏。 这是easyAI
库中给出的井字棋秘籍的一个较小变体。
创建一个新的 Python 文件并导入以下包:
from easyAI import TwoPlayersGame, AI_Player, Negamax from easyAI.Player import Human_Player
定义一个包含所有玩游戏方法的类。 首先定义玩家和谁开始游戏:
class GameController(TwoPlayersGame): def __init__(self, players): # Define the players self.players = players
# Define who starts the game self.nplayer = 1
我们将使用 3×3 的木板,编号从 1 到 9:
# Define the board self.board = [0] * 9
定义一种方法来计算所有可能的移动:
# Define possible moves def possible_moves(self): return [a + 1 for a, b in enumerate(self.board) if b == 0]
定义一种在移动后更新板的方法:
# Make a move def make_move(self, move): self.board[int(move) - 1] = self.nplayer
定义一种方法来查看是否有人输了游戏。 我们将检查某人是否连续三个:
# Does the opponent have three in a line? def loss_condition(self): possible_combinations = [[1,2,3], [4,5,6], [7,8,9], [1,4,7], [2,5,8], [3,6,9], [1,5,9], [3,5,7]]
return any([all([(self.board[i-1] == self.nopponent) for i in combination]) for combination in possible_combinations])
使用loss_condition
方法检查游戏是否完成:
# Check if the game is over def is_over(self): return (self.possible_moves() == []) or self.loss_condition()
定义一种方法来显示当前进度:
# Show current position def show(self): print('\n'+'\n'.join([' '.join([['.', 'O', 'X'][self.board[3*j + i]] for i in range(3)]) for j in range(3)]))
使用loss_condition
方法计算分数:
# Compute the score def scoring(self): return -100 if self.loss_condition() else 0
定义main
函数并从定义算法开始。 Negamax 将用作此游戏的 AI 算法。 可以预先指定算法应预先考虑的步骤数。 在这种情况下,让我们选择7
:
if __name__ == "__main__": # Define the algorithm algorithm = Negamax(7)
开始游戏:
# Start the game GameController([Human_Player(), AI_Player(algorithm)]).play()
完整代码在文件tic_tac_toe.py
中给出。 这是一个交互式游戏,您可以在计算机上玩。 如果运行代码,最初将获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdzKvVAR-1681568737507)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_03.png)]
图 3:井字棋初始输出
如果向下滚动,将看到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KeBzPCZk-1681568737507)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_04.png)]
图 4:井字棋最终输出
如我们所见,游戏以平局结束。 我们已经研究了可以与用户对抗的机器人。 现在让我们建立两个机器人来对抗。 这次在 Connect Four™ 中。
建立两个机器人来互相玩 Connect Four™
Connect Four™ 是,这是一款流行的两人游戏,以 Milton Bradley 商标出售。 它也以其他名称(例如“连续四个”或“四个向上”)而闻名。 在此游戏中,玩家轮流将光盘放入由六行七列组成的垂直网格中。 目标是连续获取四张光盘。 这是easyAI
库中提供的“连接四个”秘籍的变体。 让我们看看如何构建它。 在此秘籍中,我们将创建两个可以相互对抗的机器人,而不是与计算机对抗。 每个人将使用不同的算法来查看哪个获胜。
创建一个新的 Python 文件并导入以下包:
import numpy as np from easyAI import TwoPlayersGame, Human_Player, AI_Player, \ Negamax, SSS
定义一个类,其中包含玩游戏所需的所有方法:
class GameController(TwoPlayersGame): def __init__(self, players, board = None): # Define the players self.players = players
用六行七列定义板:
# Define the configuration of the board self.board = board if (board != None) else ( np.array([[0 for i in range(7)] for j in range(6)]))
定义谁将开始游戏。 在这种情况下,让玩家一个开始游戏:
# Define who starts the game self.nplayer = 1
定义位置:
# Define the positions self.pos_dir = np.array([[[i, 0], [0, 1]] for i in range(6)] + [[[0, i], [1, 0]] for i in range(7)] + [[[i, 0], [1, 1]] for i in range(1, 3)] + [[[0, i], [1, 1]] for i in range(4)] + [[[i, 6], [1, -1]] for i in range(1, 3)] + [[[0, i], [1, -1]] for i in range(3, 7)])
定义方法以获取所有可能的移动:
# Define possible moves def possible_moves(self): return [i for i in range(7) if (self.board[:, i].min() == 0)]
定义一种方法来控制移动:
# Define how to make the move def make_move(self, column): line = np.argmin(self.board[:, column] != 0) self.board[line, column] = self.nplayer
定义一种显示当前状态的方法:
# Show the current status def show(self): print('\n' + '\n'.join( ['0 1 2 3 4 5 6', 13 * '-'] + [' '.join([['.', 'O', 'X'][self.board[5 - j][i]] for i in range(7)]) for j in range(6)]))
定义一种方法来计算损失的外观。 每当有人连续赢得四个时,该玩家就会赢得比赛:
# Define what a loss_condition looks like def loss_condition(self): for pos, direction in self.pos_dir: streak = 0 while (0 <= pos[0] <= 5) and (0 <= pos[1] <= 6): if self.board[pos[0], pos[1]] == self.nopponent: streak += 1 if streak == 4: return True else: streak = 0
pos = pos + direction
return False
使用loss_condition
方法检查游戏是否结束:
# Check if the game is over def is_over(self): return (self.board.min() > 0) or self.loss_condition()
计算分数:
# Compute the score def scoring(self): return -100 if self.loss_condition() else 0
定义main
函数并从定义算法开始。 然后,这两种算法将相互竞争。 Negamax 将用于第一个计算机玩家,SSS*
算法将用于第二个计算机玩家。 SSS*
是一种搜索算法,它通过以最佳优先方式遍历树来进行状态空间搜索。 两种方法都将事先考虑的匝数作为输入参数。 在这种情况下,让我们同时使用5
:
if __name__ == '__main__': # Define the algorithms that will be used algo_neg = Negamax(5) algo_sss = SSS(5)
开始玩游戏:
# Start the game game = GameController([AI_Player(algo_neg), AI_Player(algo_sss)]) game.play()
打印结果:
# Print the result if game.loss_condition(): print('\nPlayer', game.nopponent, 'wins.') else: print("\nIt's a draw.")
完整代码在文件connect_four.py
中给出。 这不是一个互动游戏。 该代码将一种算法与另一种算法进行比较。 Negamax 算法是玩家 1,SSS*
算法是玩家 2。
如果运行代码,最初将获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8g7MJuVI-1681568737508)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_05.png)]
图 5:连接四个游戏的初始输出
如果向下滚动,则会在结尾处看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gIbCj9bp-1681568737508)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_06.png)]
图 6:连接四个游戏的最终输出
如我们所见,第二名玩家赢得了比赛。 让我们再尝试一个游戏:Hexapawn。
建立两个机器人来互相对抗 Hexapawn
Hexapawn 是一款两人游戏,大小为N×M
。 棋子存在于棋盘的两侧,目标是将棋子一直推进到棋盘的另一端。 国际象棋的标准典当规则适用。 这是easyAI
库中提供的 Hexapawn 秘籍的变体。 将创建两个机器人,并使其相互对峙。 让我们创建代码。
创建一个新的 Python 文件并导入以下包:
from easyAI import TwoPlayersGame, AI_Player, \ Human_Player, Negamax
定义一个类,其中包含控制游戏所需的所有方法。 首先定义两侧的棋子数和棋盘的长度。 创建一个包含位置的元组列表:
class GameController(TwoPlayersGame): def __init__(self, players, size = (4, 4)): self.size = size num_pawns, len_board = size p = [[(i, j) for j in range(len_board)] \ for i in [0, num_pawns - 1]]
为每个玩家分配方向,目标和棋子:
for i, d, goal, pawns in [(0, 1, num_pawns - 1, p[0]), (1, -1, 0, p[1])]: players[i].direction = d players[i].goal_line = goal players[i].pawns = pawns
定义玩家并指定谁先开始:
# Define the players self.players = players
# Define who starts first self.nplayer = 1
定义用于识别棋盘上的位置(例如 B6 或 C7)的字母:
# Define the alphabets self.alphabets = 'ABCDEFGHIJ'
定义一个lambda
函数将字符串转换为元组:
# Convert B4 to (1, 3) self.to_tuple = lambda s: (self.alphabets.index(s[0]), int(s[1:]) - 1)
定义lambda
函数以将元组转换为字符串:
# Convert (1, 3) to B4 self.to_string = lambda move: ' '.join([self.alphabets[ move[i][0]] + str(move[i][1] + 1) for i in (0, 1)])
定义一种方法来计算可能的移动:
# Define the possible moves def possible_moves(self): moves = [] opponent_pawns = self.opponent.pawns d = self.player.direction
如果在某个位置没有找到对手的棋子,那么这是一个有效的举动:
for i, j in self.player.pawns: if (i + d, j) not in opponent_pawns: moves.append(((i, j), (i + d, j)))
if (i + d, j + 1) in opponent_pawns: moves.append(((i, j), (i + d, j + 1)))
if (i + d, j - 1) in opponent_pawns: moves.append(((i, j), (i + d, j - 1)))
return list(map(self.to_string, [(i, j) for i, j in moves]))
定义如何进行移动并基于此更新棋子:
# Define how to make a move def make_move(self, move): move = list(map(self.to_tuple, move.split(' '))) ind = self.player.pawns.index(move[0]) self.player.pawns[ind] = move[1]
if move[1] in self.opponent.pawns: self.opponent.pawns.remove(move[1])
定义损失条件。 如果一名玩家在一行中获得 4,则对手输了:
# Define what a loss looks like def loss_condition(self): return (any([i == self.opponent.goal_line for i, j in self.opponent.pawns]) or (self.possible_moves() == []) )
使用loss_condition
方法检查游戏是否完成:
# Check if the game is over def is_over(self): return self.loss_condition()
打印当前状态:
# Show the current status def show(self): f = lambda x: '1' if x in self.players[0].pawns else ( '2' if x in self.players[1].pawns else '.')
print("\n".join([" ".join([f((i, j)) for j in range(self.size[1])]) for i in range(self.size[0])]))
定义main
函数并从定义scoring
lambda 函数开始:
if __name__=='__main__': # Compute the score scoring = lambda game: -100 if game.loss_condition() else 0
定义要使用的算法。 在这种情况下,我们将使用 Negamax,它可以预先计算12
移动并为策略使用scoring
Lambda 函数:
# Define the algorithm algorithm = Negamax(12, scoring)
开始玩游戏:
# Start the game game = GameController([AI_Player(algorithm), AI_Player(algorithm)]) game.play() print('\nPlayer', game.nopponent, 'wins after', game.nmove, 'turns')
完整代码为文件hexapawn.py
中提供的。 这不是互动游戏。 创建了两个机器人并使其相互对接。 如果运行代码,最初将获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kklkph86-1681568737508)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_07.png)]
图 7:Hexapawn 游戏的初始输出
如果您向下滚动,则会在结尾处看到以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCKgwr7o-1681568737508)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_13_08.png)]
图 8:Hexapawn 游戏最终输出
如我们所见,一名玩家赢得了比赛。
总结
在本章中,我们讨论了如何使用一种称为组合搜索的特殊类型的人工智能技术来构建游戏。 我们学习了如何使用这些类型的搜索算法来有效地提出赢得比赛的策略。 这些算法可用于为更复杂的游戏构建游戏机,并解决各种问题。 我们讨论了组合搜索以及如何使用组合搜索来加快搜索过程。 我们了解了 Minimax 和 Alpha-Beta 修剪。 我们了解了 Negamax 算法是如何在实践中使用的。 然后,我们使用这些算法来构建用于玩“最后的硬币站立”和“井字棋”的机器人。
我们学习了如何在 Connect Four 和 Hexapawn 中构建两个相互竞争的机器人。 在下一章中,我们将学习语音识别并构建一个自动识别语音的系统。
14 构建语音识别器
在本章中,我们将学习语音识别。 我们将讨论如何处理语音信号,并学习如何可视化各种音频信号。 通过利用各种技术来处理语音信号,我们将学习如何构建语音识别系统。
在本章结束时,您将了解更多有关:
- 处理语音信号
- 可视化音频信号
- 将音频信号转换到频域
- 产生音频信号
- 合成音调
- 提取语音特征
- 识别口语
我们将从讨论如何使用语音信号开始。
处理语音信号
语音识别是理解人类说出的单词的过程。 使用麦克风捕获语音信号,系统尝试理解正在捕获的单词。 语音识别广泛用于人机交互,智能手机,语音转录,生物识别系统,安全性等。
在分析语音信号之前,了解其本质非常重要。 这些信号恰好是各种信号的复杂混合。 语音的许多不同方面都会导致其复杂性。 它们包括情感,口音,语言和噪音。
由于这种复杂性,很难定义一套可靠的规则来分析语音信号。 相反,即使语音可以有很多变化,人类在理解语音方面也很出色。 人类似乎相对容易做到这一点。 为了使机器做到相同,我们需要帮助他们以与人类相同的方式理解语音。
研究人员致力于语音的各个方面和应用,例如理解口语,识别说话者是谁,识别情感以及识别口音。 在本章中,我们将着重于理解口语。 语音识别代表了人机交互领域的重要一步。 如果我们想构建可以与人类互动的认知机器人,那么他们需要以自然语言与我们对话。 这就是近年来自动语音识别成为许多研究人员关注的焦点的原因。 让我们继续,看看如何处理第个语音信号并构建语音识别器。
可视化音频信号
让我们看看如何可视化音频信号。 我们将学习如何从文件中读取音频信号并进行处理。 这将帮助我们了解音频信号的结构。 使用麦克风录制音频文件时,它们会采样实际的音频信号并存储数字化版本。 真实的音频信号是连续的值波,这意味着我们无法按原样存储它们。 我们需要以一定频率对信号进行采样并将其转换为离散的数值形式。
最常见的是,语音信号以 44,100 Hz 采样。 这意味着语音信号的每一秒被分解成 44,100 个部分,并且这些时间戳中每个时间戳的值都存储在输出文件中。 我们每1/44,100
秒保存一次音频信号的值。 在这种情况下,我们说音频信号的采样频率为 44,100 Hz。 通过选择高采样频率,当人们听音频信号时,它似乎是连续的。 让我们继续进行可视化音频信号。
创建一个新的 Python 文件并导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile
使用wavefile.read
方法读取输入的音频文件。 它返回两个值–采样频率和音频信号:
# Read the audio file sampling_freq, signal = wavfile.read('random_sound.wav')
打印信号的形状,数据类型和音频信号的持续时间:
# Display the params print('\nSignal shape:', signal.shape) print('Datatype:', signal.dtype) print('Signal duration:', round(signal.shape[0] / float(sampling_freq), 2), 'seconds')
标准化信号:
# Normalize the signal signal = signal / np.power(2, 15)
从numpy
数组中提取第一个50
值进行绘图:
# Extract the first 50 values signal = signal[:50]
以秒为单位构造时间轴:
# Construct the time axis in milliseconds time_axis = 1000 * np.arange(0, len(signal), 1) / float(sampling_freq)
绘制音频信号:
# Plot the audio signal plt.plot(time_axis, signal, color='black') plt.xlabel('Time (milliseconds)') plt.ylabel('Amplitude') plt.title('Input audio signal') plt.show()
完整代码在文件audio_plotter.py
中给出。 如果运行代码,您将看到以下屏幕截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9heU3lq-1681568737509)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_01.png)]
图 1:输入音频信号的可视化
前面的屏幕截图显示了输入音频信号的前 50 个样本。 您将看到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRKeJ8vi-1681568737509)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_02.png)]
图 2:输入音频信号输出
上图中打印的输出显示了我们从符号 al 中提取的信息。
将音频信号转换到频域
为了分析音频信号,我们需要了解基本的频率成分。 这使我们能够洞悉如何从该信号中提取有意义的信息。 音频信号由频率,相位和幅度不同的正弦波混合而成。
如果我们剖析频率分量,我们可以识别很多特征。 任何给定的音频信号的特征在于其在频谱中的分布。 为了将时域信号转换为频域,我们需要使用数学工具,例如傅里叶变换。 如果您需要快速学习傅立叶变换,请查看以下链接。 让我们看看如何将音频信号从时域转换到频域。
创建一个新的 Python 文件并导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile
使用wavefile.read
方法读取输入的音频文件。 它返回两个值–采样频率和音频信号:
# Read the audio file sampling_freq, signal = wavfile.read('spoken_word.wav')
标准化音频信号:
# Normalize the values signal = signal / np.power(2, 15)
提取信号的长度和一半长度:
# Extract the length of the audio signal len_signal = len(signal)
# Extract the half length len_half = np.ceil((len_signal + 1) / 2.0).astype(np.int)
对信号应用傅立叶变换:
# Apply Fourier transform freq_signal = np.fft.fft(signal)
归一化频域信号并取平方:
# Normalization freq_signal = abs(freq_signal[0:len_half]) / len_signal
# Take the square freq_signal **= 2
调整偶数和奇数情况下的傅立叶变换信号:
# Extract the length of the frequency transformed signal len_fts = len(freq_signal)
# Adjust the signal for even and odd cases if len_signal % 2: freq_signal[1:len_fts] *= 2
else: freq_signal[1:len_fts-1] *= 2
在dB
中提取电源信号:
# Extract the power value in dB signal_power = 10 * np.log10(freq_signal)
构建X
轴,在这种情况下,是在kHz
中测量的频率:
# Build the X axis x_axis = np.arange(0, len_half, 1) * (sampling_freq / len_signal) / 1000.0
绘制图:
# Plot the figure plt.figure() plt.plot(x_axis, signal_power, color='black') plt.xlabel('Frequency (kHz)') plt.ylabel('Signal power (dB)') plt.show()
完整代码在文件frequency_transformer.py
中给出。 如果运行代码,您将看到以下屏幕截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yS2pNNBx-1681568737509)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_03.png)]
图 3:音频信号转换的可视化
前面的屏幕截图显示了信号在整个频谱上的强大程度。 在这种情况下,信号的功率会以较高的频率下降。
生成音频信号
现在我们知道音频信号是如何工作的,让我们看看如何生成一个这样的信号。 我们可以使用 NumPy 包生成各种音频信号。 由于音频信号是正弦波的混合,我们可以使用它来生成具有一些预定义参数的音频信号。
创建一个新的 Python 文件并导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io.wavfile import write
定义输出音频文件的名称:
# Output file where the audio will be saved output_file = 'generated_audio.wav'
指定音频参数,例如持续时间,采样频率,音频频率,最小值和最大值:
# Specify audio parameters duration = 4 # in seconds sampling_freq = 44100 # in Hz tone_freq = 784 min_val = -4 * np.pi max_val = 4 * np.pi
使用定义的参数生成音频信号:
# Generate the audio signal t = np.linspace(min_val, max_val, duration * sampling_freq) signal = np.sin(2 * np.pi * tone_freq * t)
给信号增加一些噪声:
# Add some noise to the signal noise = 0.5 * np.random.rand(duration * sampling_freq) signal += noise
归一化并缩放信号:
# Scale it to 16-bit integer values scaling_factor = np.power(2, 15) - 1 signal_normalized = signal / np.max(np.abs(signal)) signal_scaled = np.int16(signal_normalized * scaling_factor)
将生成的音频信号保存在输出文件中:
# Save the audio signal in the output file write(output_file, sampling_freq, signal_scaled)
提取第一个200
值进行绘图:
# Extract the first 200 values from the audio signal signal = signal[:200]
构造时间轴(以毫秒为单位):
# Construct the time axis in milliseconds time_axis = 1000 * np.arange(0, len(signal), 1) / float(sampling_freq)
绘制音频信号:
# Plot the audio signal plt.plot(time_axis, signal, color='black') plt.xlabel('Time (milliseconds)') plt.ylabel('Amplitude') plt.title('Generated audio signal') plt.show()
完整代码在文件audio_generator.py
中给出。 如果运行代码,您将看到以下屏幕截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uy5Kftzf-1681568737509)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_04.png)]
图 4:音频信号生成的可视化
使用媒体播放器播放文件generated_audio.wav
,以查看的外观。 这将是 784 Hz 信号和噪声信号的混合信号。
合成音调以产生音乐
前面的部分介绍了如何生成简单的单调,但意义不大。 它只是信号中的单个频率。 让我们使用该原理通过将不同的音调拼接在一起来合成音乐。 我们将使用诸如A
,C
,G
和F
之类的标准音来生成音乐。 为了查看这些标准音调的频率映射,请查看以下链接。
让我们使用此信息来生成音乐信号。
创建一个新的 Python 文件并导入以下包:
import json import numpy as np import matplotlib.pyplot as plt from scipy.io.wavfile import write
定义一个函数以根据输入参数生成音调:
# Synthesize the tone based on the input parameters def tone_synthesizer(freq, duration, amplitude=1.0, sampling_freq=44100): # Construct the time axis time_axis = np.linspace(0, duration, duration * sampling_freq)
使用指定的参数构造音频信号并返回:
# Construct the audio signal signal = amplitude * np.sin(2 * np.pi * freq * time_axis) return signal.astype(np.int16)
定义main
函数。 让我们定义输出音频文件名:
if __name__=='__main__': # Names of output files file_tone_single = 'generated_tone_single.wav' file_tone_sequence = 'generated_tone_sequence.wav'
我们将使用一个音调映射文件,其中包含从音调名称(例如A
,C
和G
)到相应频率的映射:
# Source: http://www.phy.mtu.edu/~suits/notefreqs.html mapping_file = 'tone_mapping.json'
# Load the tone to frequency map from the mapping file with open(mapping_file, 'r') as f: tone_map = json.loads(f.read())
让我们生成持续时间为3
秒的F
音调:
# Set input parameters to generate 'F' tone tone_name = 'F' # seconds duration = 3 # amplitude amplitude = 12000 # Hz sampling_freq = 44100
提取相应的音频频率:
# Extract the tone frequency tone_freq = tone_map[tone_name]
使用前面定义的音调合成器函数生成音调:
# Generate the tone using the above parameters synthesized_tone = tone_synthesizer(tone_freq, duration, amplitude, sampling_freq)
将生成的音频信号写入输出文件:
# Write the audio signal to the output file write(file_tone_single, sampling_freq, synthesized_tone)
让我们生成一个音调序列,使其听起来像音乐。 让我们定义一个音调序列,其持续时间以秒为单位:
# Define the tone sequence along with corresponding # durations in seconds tone_sequence = [('G', 0.4), ('D', 0.5), ('F', 0.3), ('C', 0.6), ('A', 0.4)]
根据音调序列构造音频信号:
# Construct the audio signal based on the above sequence signal = np.array([]) for item in tone_sequence: # Get the name of the tone tone_name = item[0]
对于每个音调,提取相应的频率:
# Extract the corresponding frequency of the tone freq = tone_map[tone_name]
提取相应的持续时间:
# Extract the duration duration = item[1]
使用tone_synthesizer
函数合成音色:
# Synthesize the tone synthesized_tone = tone_synthesizer(freq, duration, amplitude, sampling_freq)
将其附加到主输出信号:
# Append the output signal signal = np.append(signal, synthesized_tone, axis=0)
将主输出信号保存到输出文件:
# Save the audio in the output file write(file_tone_sequence, sampling_freq, signal)
完整代码为文件synthesizer.py
中提供的。 如果运行代码,它将生成两个输出文件-generated_tone_single.wav
和generated_tone_sequence.wav
。
您可以使用媒体播放器播放音频文件,以听一下的声音。
提取语音特征
我们学习了如何将时域信号转换为频域。 频域特征已在所有语音识别系统中广泛使用。 我们之前讨论的概念是对该概念的介绍,但实际的频域特征要复杂一些。 将信号转换到频域后,我们需要确保以特征向量的形式使用该信号。 这就是 梅尔频谱系数(MFCC)的概念。 MFCC 是工具,用于从给定音频信号中提取频域特征。
为了从音频信号中提取频率特征,MFCC 首先提取功率谱。 然后,它使用过滤器组和离散余弦变换(DCT)提取特征。 如果您有兴趣进一步研究 MFCC,请查看以下链接。
我们将使用一个名为python_speech_features
的包来提取 MFCC 特征。 该包在这里可用。
为了易于使用,相关的文件夹已包含在代码包中。 您将在代码包中看到一个名为features
的文件夹,其中包含使用此包所需的文件。 让我们看看如何提取 MFCC 特征。
创建一个新的 Python 文件并导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile from python_speech_features import mfcc, logfbank
读取输入的音频文件并提取第一个10,000
样本进行分析:
# Read the input audio file sampling_freq, signal = wavfile.read('random_sound.wav')
# Take the first 10,000 samples for analysis signal = signal[:10000]
提取 MFCC:
# Extract the MFCC features features_mfcc = mfcc(signal, sampling_freq)
打印 MFCC 参数:
# Print the parameters for MFCC print('\nMFCC:\nNumber of windows =', features_mfcc.shape[0]) print('Length of each feature =', features_mfcc.shape[1])
绘制 MFCC 特征:
# Plot the features features_mfcc = features_mfcc.T plt.matshow(features_mfcc) plt.title('MFCC')
提取过滤器组特征:
# Extract the Filter Bank features features_fb = logfbank(signal, sampling_freq)
打印过滤器组的参数:
# Print the parameters for Filter Bank print('\nFilter bank:\nNumber of windows =', features_fb.shape[0]) print('Length of each feature =', features_fb.shape[1])
绘制特征:
# Plot the features features_fb = features_fb.T plt.matshow(features_fb) plt.title('Filter bank')
plt.show()
完整代码在文件feature_extractor.py
中给出。 如果运行代码,您将看到两个屏幕截图。 第一个屏幕截图显示了 MFCC 特征:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mghWPicW-1681568737510)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_05.png)]
图 5:MFCC 特征映射
第二张屏幕截图显示了过滤器组特征:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cz10JOI3-1681568737510)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_06.png)]
图 6:过滤器组特征映射
您将看到以下打印出来:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Uym6i1r-1681568737510)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_07.png)]
图 7:MFCC 和过滤器组特征输出
正如我们在上一张图表中所看到的以及在本章中学到的那样,将声音转换为图片可能非常有用,并且它可以让我们以不同的方式分析声音并得出我们否则会错过的有见识的见解。
识别口语
现在已经学习了分析语音信号的所有技术,让我们继续学习如何识别语音。 语音识别系统将音频信号作为输入并识别正在说的单词。 隐马尔可夫模型(HMM)将用于此任务。
正如我们在上一章中讨论的那样,HMM 非常适合分析序列数据。 音频信号是时间序列信号,是序列数据的体现。 假定输出是由系统经过一系列隐藏状态生成的。 我们的目标是找出这些隐藏状态是什么,以便我们可以识别信号中的单词。 如果您有兴趣深入研究,请查看以下链接。
我们将使用名为hmmlearn
的包来构建我们的语音识别系统。 您可以在此处了解更多信息。
您可以通过运行以下命令来安装包:
$ pip3 install hmmlearn
为了训练我们的语音识别系统,我们需要每个单词的音频文件数据集。 我们将使用这个页面上可用的数据库。
为了易于使用,在代码包中为您提供了一个名为data
的文件夹,其中包含所有这些文件。 该数据集包含七个不同的单词。 每个词都有一个与之关联的文件夹,每个文件夹有 15 个音频文件。
在每个文件夹中,我们将使用 14 个进行训练,使用 1 个进行测试。 请注意,这实际上是一个非常小的数据集。 在现实世界中,您将使用更大的数据集来构建语音识别系统。 我们正在使用该数据集来熟悉语音识别,并了解如何构建一个系统来识别语音。
我们将为每个单词建立一个 HMM 模型,并存储所有模型以供参考。 当我们想识别未知音频文件中的单词时,我们将在所有这些模型中运行该单词,并选择得分最高的单词。 让我们看看如何建立这个系统。
创建一个新的 Python 文件并导入以下包:
import os import argparse import warnings
import numpy as np from scipy.io import wavfile
from hmmlearn import hmm from python_speech_features import mfcc
定义函数解析输入参数。 我们需要指定输入文件夹,其中包含训练语音识别系统所需的音频文件:
# Define a function to parse the input arguments def build_arg_parser(): parser = argparse.ArgumentParser(description='Trains the HMM-based speech recognition system') parser.add_argument("--input-folder", dest="input_folder", required=True, help="Input folder containing the audio files for training") return parser
定义一个训练 HMM 的类:
# Define a class to train the HMM class ModelHMM(object): def __init__(self, num_components=4, num_iter=1000): self.n_components = num_components self.n_iter = num_iter
定义协方差类型和 HMM 类型:
self.cov_type = 'diag' self.model_name = 'GaussianHMM'
初始化变量,我们将在其中存储每个单词的模型:
self.models = []
使用指定的参数定义模型:
self.model = hmm.GaussianHMM(n_components=self.n_components, covariance_type=self.cov_type, n_iter=self.n_iter)
定义训练模型的方法:
# 'training_data' is a 2D numpy array where each row is 13-dimensional def train(self, training_data): np.seterr(all='ignore') cur_model = self.model.fit(training_data) self.models.append(cur_model)
定义一种方法来计算输入数据的分数:
# Run the HMM model for inference on input data def compute_score(self, input_data): return self.model.score(input_data)
定义一个函数为训练数据集中的每个单词构建模型:
# Define a function to build a model for each word def build_models(input_folder): # Initialize the variable to store all the models speech_models = []
解析输入目录:
# Parse the input directory for dirname in os.listdir(input_folder): # Get the name of the subfolder subfolder = os.path.join(input_folder, dirname) if not os.path.isdir(subfolder): continue
提取标签:
# Extract the label label = subfolder[subfolder.rfind('/') + 1:]
初始化变量以存储训练数据:
# Initialize the variables X = np.array([])
创建用于训练的文件列表:
# Create a list of files to be used for training # We will leave one file per folder for testing training_files = [x for x in os.listdir(subfolder) if x.endswith('.wav')][:-1]
# Iterate through the training files and build the models for filename in training_files: # Extract the current filepath filepath = os.path.join(subfolder, filename)
从当前文件读取音频信号:
# Read the audio signal from the input file sampling_freq, signal = wavfile.read(filepath)
提取 MFCC 特征:
# Extract the MFCC features with warnings.catch_warnings(): warnings.simplefilter('ignore') features_mfcc = mfcc(signal, sampling_freq)
将数据点附加到变量X
:
# Append to the variable X if len(X) == 0: X = features_mfcc else: X = np.append(X, features_mfcc, axis=0)
初始化 HMM 模型:
# Create the HMM model model = ModelHMM()
使用训练数据训练模型:
# Train the HMM model.train(X)
将模型保存为当前单词:
# Save the model for the current word speech_models.append((model, label))
# Reset the variable model = None
return speech_models
定义一个函数以在测试数据集上运行测试:
# Define a function to run tests on input files def run_tests(test_files): # Classify input data for test_file in test_files: # Read input file sampling_freq, signal = wavfile.read(test_file)
提取 MFCC 特征:
# Extract MFCC features with warnings.catch_warnings(): warnings.simplefilter('ignore') features_mfcc = mfcc(signal, sampling_freq)
定义变量以存储最高分数和输出标签:
# Define variables max_score = -float('inf') output_label = None
遍历每种模型以选择最佳模型:
# Run the current feature vector through all the HMM # models and pick the one with the highest score for item in speech_models: model, label = item
求解分数并与最高分数进行比较:
score = model.compute_score(features_mfcc) if score > max_score: max_score = score predicted_label = label
打印输出:
# Print the predicted output start_index = test_file.find('/') + 1 end_index = test_file.rfind('/') original_label = test_file[start_index:end_index] print('\nOriginal: ', original_label) print('Predicted:', predicted_label)
定义main
函数并从输入参数获取输入文件夹:
if __name__=='__main__': args = build_arg_parser().parse_args() input_folder = args.input_folder
为输入文件夹中的每个单词建立一个 HMM 模型:
# Build an HMM model for each word speech_models = build_models(input_folder)
我们在每个文件夹中保留了一个文件进行测试。 使用该文件来查看模型的准确率:
# Test files -- the 15th file in each subfolder test_files = [] for root, dirs, files in os.walk(input_folder): for filename in (x for x in files if '15' in x): filepath = os.path.join(root, filename) test_files.append(filepath)
run_tests(test_files)
完整代码在文件speech_recognizer.py
中给出。 确保data
文件夹与代码文件位于同一文件夹中。 运行代码,如下所示:
$ python3 speech_recognizer.py --input-folder data
如果运行代码,将看到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMZ8oxp7-1681568737510)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_14_08.png)]
图 8:识别的单词输出
正如我们在前面的编辑屏幕截图中所见一样,我们的语音识别系统可以正确识别的所有单词。
总结
在本章中,我们学习了语音识别。 我们讨论了如何使用语音信号和相关概念。 我们学习了如何可视化音频信号。 我们讨论了如何使用傅立叶变换将时域音频信号转换为频域。 我们讨论了如何使用预定义的参数生成音频信号。
然后,我们使用此概念通过将音调缝合在一起来合成音乐。 我们讨论了 MFCC 及其在现实世界中的使用方式。 我们了解了如何从语音中提取频率特征。 我们学习了如何使用所有这些技术来构建语音识别系统。 在下一章中,我们将讨论自然语言处理以及如何通过建模并对其进行分类来分析文本数据。
15 自然语言处理
在本章中,我们将学习自然语言处理(NLP)的令人兴奋的主题。 正如我们在前几章中所讨论的,拥有能够理解人类语言的计算机是真正使计算机变得更加有用的突破之一。 NLP 为开始了解如何实现提供了基础。
我们将讨论并使用各种概念(例如标记化,词干和词形还原)来处理文本。 然后,我们将讨论词袋模型以及如何使用它对文本进行分类。 我们将看到如何使用机器学习来分析句子的情感。 然后,我们将讨论主题建模并实现一个系统来识别给定文档中的主题。
在本章结束时,您将熟悉以下主题:
- 安装相关的 NLP 包
- 标记文本数据
- 使用词干提取将单词转换为其基本形式
- 使用词形还原将单词转换为其基本形式
- 将文本数据分成块
- 使用词袋模型提取文档项矩阵
- 建立类别预测器
- 构造性别标识符
- 建立情感分析器
- 使用潜在狄利克雷分布的主题建模
包的介绍和安装
自然语言处理(NLP)已成为现代系统的重要组成部分。 它广泛用于搜索引擎,会话界面,文档处理器等。 机器可以很好地处理结构化数据,但是在处理自由格式的文本时,它们会遇到困难。 NLP 的目标是开发使计算机能够理解自由格式文本并帮助他们理解语言的算法。
自由处理-形式自然语言最具挑战性的之一是数量众多。 上下文在句子的理解中起着非常重要的作用。 人类天生善于理解语言。 尚不清楚人类如何轻松而直观地理解语言。 我们利用我们过去的知识和经验来理解对话,即使在几乎没有明确上下文的情况下,我们也可以迅速了解其他人在谈论什么。
为了解决这个问题,NLP 研究人员开始使用机器学习方法来开发各种应用。 为了构建这样的应用,需要获取大量的文本,然后在该数据上训练算法以执行各种任务,例如对文本进行分类,分析情感和对主题进行建模。 对算法进行训练,以检测输入文本数据中的模式并从中获取见解。
在本章中,我们将讨论用于分析文本和构建 NLP 应用的各种基础概念。 这将使我们了解如何从给定的文本数据中提取有意义的信息。 我们将使用称为自然语言工具包(NLTK)的 Python 包来构建这些应用。 您可以通过运行以下命令来安装它:
$ pip3 install nltk
您可以在这个页面上找到有关 NLTK 的更多信息。
为了访问 NLTK 提供的所有数据集,我们需要下载它。 通过键入以下内容打开 Python Shell:
$ python3
现在,我们位于 Python Shell 中。 输入以下内容以下载数据:
>>> import nltk >>> nltk.download()
在本章中,我们还将使用名为gensim
的包。 gensim
是健壮的语义建模库,对许多应用都非常有用。 可以通过运行以下命令来安装它:
$ pip3 install gensim
您可能需要另一个包pattern
才能使gensim
正常运行。 您可以通过运行以下命令来安装它:
$ pip3 install pattern
您可以在这个页面上找到有关 Gensim 的更多信息。 现在,您已经安装了 NLTK 和gensim
,让我们继续进行讨论。
对文本数据分词
当我们处理文本时,我们需要将其分解成较小的部分进行分析。 为此,可以应用分词。 分词是将文本分为一组片段的过程,例如单词或句子。 这些片段称为令牌。 根据我们要执行的操作,我们可以定义自己的方法以将文本分为许多标记。 让我们看一下如何使用 NLTK 对输入文本分词。
创建一个新的 Python 文件并导入以下包:
from nltk.tokenize import sent_tokenize, \ word_tokenize, WordPunctTokenizer
定义将用于分词的输入文本:
# Define input text input_text = "Do you know how tokenization works? It's actually \ quite interesting! Let's analyze a couple of sentences and \ figure it out."
将输入文本分为句子标记:
# Sentence tokenizer print("\nSentence tokenizer:") print(sent_tokenize(input_text))
将输入文本分为单词标记:
# Word tokenizer print("\nWord tokenizer:") print(word_tokenize(input_text))
使用WordPunct
分词器将输入文本分为单词标记:
# WordPunct tokenizer print("\nWord punct tokenizer:") print(WordPunctTokenizer().tokenize(input_text))
完整代码在文件tokenizer.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kn0kHmH-1681568737511)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_01.png)]
图 1:分词器输出
句子分词程序将输入文本分为句子。 当涉及标点时,两个单词的分词器的行为会有所不同。 例如,单词It's
由标点分词器划分的方式与常规分词器的划分方式不同。
使用词干提取将单词转换为其基本形式
使用文本意味着需要进行很多变化。 我们必须处理同一个单词的不同形式,并使计算机理解这些不同的单词具有相同的基本形式。 例如,单词sing
可以以多种形式出现,例如singer, singing, song, sung
等。 这组单词具有相似的含义。 此过程称为词干提取。 词干是产生词根/基词形态变异的一种方式。 人类可以轻松地识别这些基本形式并得出上下文。
分析文本时,提取这些基本形式很有用。 这样做可以提取从输入文本中导出的有用统计信息。 词干提取是实现此目的的一种方法。 词干提取器的目标是将单词从其不同形式简化为通用的基本形式。 基本上,这是一种启发式过程,可切断单词的结尾以提取其基本形式。 让我们看看如何使用 NLTK 做到这一点。
创建一个新的 Python 文件并导入以下包:
from nltk.stem.porter import PorterStemmer from nltk.stem.lancaster import LancasterStemmer from nltk.stem.snowball import SnowballStemmer
定义一些输入词:
input_words = ['writing', 'calves', 'be', 'branded', 'horse', 'randomize', 'possibly', 'provision', 'hospital', 'kept', 'scratchy', 'code']
为Porter
,Lancaster
和Snowball
词干提取器创建对象:
# Create various stemmer objects porter = PorterStemmer() lancaster = LancasterStemmer() snowball = SnowballStemmer('english')
创建用于表显示的名称列表,并相应地格式化输出文本:
# Create a list of stemmer names for display stemmer_names = ['PORTER', 'LANCASTER', 'SNOWBALL'] formatted_text = '{:>16}' * (len(stemmer_names) + 1) print('\n', formatted_text.format('INPUT WORD', *stemmer_names), '\n', '='*68)
遍历单词,并使用以下三个词干器对其进行词干提取:
# Stem each word and display the output for word in input_words: output = [word, porter.stem(word), lancaster.stem(word), snowball.stem(word)] print(formatted_text.format(*output))
完整代码在文件stemmer.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OVO40N5S-1681568737511)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_02.png)]
图 2:语音输出
让我们讨论一下这里使用的三种词干算法。 他们基本上都试图实现相同的目标。 它们之间的区别是用来达到基本形式的严格程度。
Porter
词干分析器最不严格,而Lancaster
最严格。 如果仔细观察输出,您会发现差异。 对于possibly
或provision
之类的词,词干的行为有所不同。 从Lancaster
词干提取器获得的词干输出有些混乱,因为它大大减少了单词数。 同时,该算法速度很快。 有一个很好的经验法则是使用Snowball
提取器,因为它是速度和严格性之间的良好权衡。
使用词形还原将单词转换为其基本形式
词形还原是将单词还原为基本形式的另一种方法。 在上一节中,我们看到了从这些词干提取的一些基本形式没有意义。 词形还原是将单词的不同变形形式组合在一起的过程,因此可以将它们作为单个项目进行分析。 词形还原就像词干提取一样,但是却为这些词带来了语境。 因此,它将具有相似含义的单词链接到一个单词。 例如,所有三个词干都说calves
的基本形式是calv
,这不是一个真实的词。 词形还原采用一种更加结构化的方法来解决此问题。 以下是一些词形还原的示例:
rocks : rock
corpora : corpus
worse : bad
词形还原过程使用单词的词法和词法分析。 它通过删除诸如ing
或ed
之类的词尾变化来获得基本形式。 任何单词的基本形式都称为词形。 如果对calves
一词进行词形还原,则应该得到calf
作为输出。 需要注意的一件事是,输出取决于单词是动词还是名词。 让我们看看如何使用 NLTK 做到这一点。
创建一个新的 Python 文件并导入以下包:
from nltk.stem import WordNetLemmatizer
定义一些输入词。 我们将使用与上一节相同的一组单词,以便我们可以比较输出:
input_words = ['writing', 'calves', 'be', 'branded', 'horse', 'randomize', 'possibly', 'provision', 'hospital', 'kept', 'scratchy', 'code']
创建一个lemmatizer
对象:
# Create lemmatizer object lemmatizer = WordNetLemmatizer()
为表显示创建lemmatizer
名称的列表,并相应地设置文本格式:
# Create a list of lemmatizer names for display lemmatizer_names = ['NOUN LEMMATIZER', 'VERB LEMMATIZER'] formatted_text = '{:>24}' * (len(lemmatizer_names) + 1) print('\n', formatted_text.format('INPUT WORD', *lemmatizer_names), '\n', '='*75)
遍历单词并使用名词和动词词形还原器对单词进行词形还原:
# Lemmatize each word and display the output for word in input_words: output = [word, lemmatizer.lemmatize(word, pos='n'), lemmatizer.lemmatize(word, pos='v')] print(formatted_text.format(*output))
完整代码在文件lemmatizer.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGrl2Zry-1681568737511)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_03.png)]
图 3:词形还原器输出
我们可以看到,名词词形还原器的作用与动词词形还原器的作用不同,例如涉及writing
或calves
的单词。 如果将这些输出与词干提取输出进行比较,则也会看到差异。 词形还原器的输出都是有意义的,而词干提取器的输出可能是有意义的也可能是没意义的。
将文本数据分为多个块
通常需要将文本数据分成多个部分以进行进一步分析。 该处理称为分块。 这在文本分析中经常使用。 用于将文本分为几部分的条件会根据当前问题而有所不同。 这与标记化不同,在标记化中,文本也分为几部分。 在分块期间,除了输出分块需要有意义之外,我们没有遵守任何约束条件。
当我们处理大型文本文档时,将文本分成大块以提取有意义的信息变得很重要。 在本节中,我们将看到如何将输入文本分为几部分。
创建一个新的 Python 文件并导入以下包:
import numpy as np from nltk.corpus import brown
定义一个函数,将输入文本分为多个块。 第一个参数是文本,第二个参数是每个块中的单词数:
# Split the input text into chunks, where # each chunk contains N words def chunker(input_data, N): input_words = input_data.split(' ') output = []
遍历单词,并使用输入参数将它们分成大块。 该函数返回一个列表:
cur_chunk = [] count = 0 for word in input_words: cur_chunk.append(word) count += 1 if count == N: output.append(' '.join(cur_chunk)) count, cur_chunk = 0, [] output.append(' '.join(cur_chunk)) return output
定义主函数并使用 Brown 语料库读取输入数据。 在这种情况下,我们将读取12000
字。 您可以随意阅读任意多个单词:
if __name__=='__main__': # Read the first 12000 words from the Brown corpus input_data = ' '.join(brown.words()[:12000])
定义每个块中的单词数:
# Define the number of words in each chunk chunk_size = 700
将输入文本分成大块并显示输出:
chunks = chunker(input_data, chunk_size) print('\nNumber of text chunks =', len(chunks), '\n') for i, chunk in enumerate(chunks): print('Chunk', i+1, '==>', chunk[:50])
完整代码在文件text_chunker.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVtq78Xq-1681568737511)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_04.png)]
图 4:文本分块器输出
上面的屏幕快照显示了每个块的前 50 个字符。
现在,我们已经探索了将文本分割和分块的技术,让我们开始研究执行文本分析的方法。
使用“词袋”模型提取词频
词袋模型的文本分析的主要目标之一是将文本转换为数字形式,以便我们可以在其上使用机器学习。 让我们考虑包含数百万个单词的文本文档。 为了分析这些文档,我们需要提取文本并将其转换为数字表示形式。
机器学习算法需要使用数字数据,以便它们可以分析数据并提取有意义的信息。 这就是词袋模型的用处。该模型从文档中的所有单词中提取词汇,并使用文档项矩阵构建模型。 这使我们可以将每个文档表示为词袋。 我们只是跟踪字数,而忽略语法细节和字序。
让我们看看文档项矩阵的含义。 文档项矩阵基本上是一个表,它为我们提供了文档中出现的各种单词的计数。 因此,文本文档可以表示为各个单词的加权组合。 我们可以设置阈值并选择更有意义的词。 在某种程度上,我们正在构建文档中所有单词的直方图,并将其用作特征向量。 该特征向量用于文本分类。
考虑以下句子:
- 句子 1:
The children are playing in the hall
(孩子们在大厅玩耍) - 句子 2:
The hall has a lot of space
(大厅有很多空间) - 句子 3:
Lots of children like playing in an open space
(很多孩子喜欢在露天场所玩耍)
如果您考虑所有三个句子,我们有以下 14 个唯一词:
the
children
(孩子们)are
(是)playing
(玩耍)in
(在)hall
(大厅)has
(有)a
(一个)lot
(很多)of
(的)space
(空间)like
(喜欢)an
(一个)open
(露天)
让我们通过使用每个句子中的单词计数为每个句子构造一个直方图。 每个特征向量都是 14 维的,因为我们有 14 个唯一的词:
- 句子 1:
[2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
- 句子 2:
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
- 句子 3:
[0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1]
现在,我们已经使用“词袋”模型提取了这些特征,我们可以使用机器学习算法来分析这些数据。
让我们看看如何在 NLTK 中构建“语言袋”模型。 创建一个新的 Python 文件并导入以下包:
import numpy as np from sklearn.feature_extraction.text import CountVectorizer from nltk.corpus import brown from text_chunker import chunker
从 Brown 语料库读取输入数据。 我们将使用5,400
字。 随意尝试使用尽可能多的单词:
# Read the data from the Brown corpus input_data = ' '.join(brown.words()[:5400])
定义每个块中的单词数:
# Number of words in each chunk chunk_size = 800
将输入文本分成多个块:
text_chunks = chunker(input_data, chunk_size)
将这些块转换成字典项:
# Convert to dict items chunks = [] for count, chunk in enumerate(text_chunks): d = {'index': count, 'text': chunk} chunks.append(d)
提取文档项矩阵,从中获得每个单词的计数。 我们将使用CountVectorizer
方法来实现这一点,该方法需要两个输入参数。 第一个参数是最小文档频率,第二个参数是最大文档频率。 频率是指单词在文本中出现的次数:
# Extract the document term matrix count_vectorizer = CountVectorizer(min_df=7, max_df=20) document_term_matrix = count_vectorizer.fit_transform([chunk['text'] for chunk in chunks])
用词袋模型提取词汇表并显示它。 词汇表是在上一步中提取的不同单词的列表:
# Extract the vocabulary and display it vocabulary = np.array(count_vectorizer.get_feature_names()) print("\nVocabulary:\n", vocabulary)
生成显示名称:
# Generate names for chunks chunk_names = [] for i in range(len(text_chunks)): chunk_names.append('Chunk-' + str(i+1))
打印文档项矩阵:
# Print the document term matrix print("\nDocument term matrix:") formatted_text = '{:>12}' * (len(chunk_names) + 1) print('\n', formatted_text.format('Word', *chunk_names), '\n') for word, item in zip(vocabulary, document_term_matrix.T): # 'item' is a 'csr_matrix' data structure output = [word] + [str(freq) for freq in item.data] print(formatted_text.format(*output))
完整代码在文件bag_of_words.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISoqB5w7-1681568737511)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_05.png)]
图 5:文档项矩阵输出
可以在词袋模型文档项矩阵中看到所有单词以及每个块中相应的自发计数。
现在我们已经对单词进行了计数,我们可以在此基础上开始基于单词的频率做出一些预测。
建立类别预测变量
类别预测器用于预测给定文本所属的类别。 这在文本分类中经常用于对文本文档进行分类。 搜索引擎经常使用此工具按相关性对搜索结果进行排序。 例如,假设我们要预测给定的句子是属于体育,政治还是科学。 为此,我们建立了一个数据集并训练了一个算法。 然后可以将该算法用于推断未知数据。
为了构建此预测变量,我们将使用一个称为词频-文档反向频率(tf-idf)的度量。 在文档集中,我们需要了解每个单词的重要性。 tf-idf 指标可帮助我们了解给定单词对文档集中的文档 t 的重要性。
让我们考虑该指标的的第一部分。 词频(tf)基本上是,用于衡量每个单词在给定文档中出现的频率。 由于不同的文档具有不同的单词数,因此直方图中的确切数字将有所不同。 为了有一个公平的竞争环境,我们需要标准化直方图。 因此,我们将每个单词的计数除以给定文档中的单词总数,以获得词频。
度量的第二部分是,反向文档频率(idf),它是一个单词在给定文档集中对文档的唯一性的度量。 当我们计算频率一词时,假设所有单词都同等重要。 但是我们不能仅仅依靠每个单词的频率,因为and, or, the
以及等单词的出现很多。 为了平衡这些常见单词的频率,我们需要减少它们的权重并增加稀有单词的权重。 这也有助于我们识别每个文档唯一的单词,从而帮助我们制定独特的特征向量。
要计算此统计信息,我们需要计算具有给定单词的文档数量的比率,并将其除以文档总数。 该比率实质上是包含给定单词的文档的一部分。 然后通过采用该比率的负算法来计算文档的逆向频率。
然后,我们将词频和文档的逆频率结合起来,以制定特征向量来对文档进行分类。 这项工作是深入分析文本以获得更深层含义的基础,例如情感分析,文本上下文或主题分析。 让我们看看如何构建类别预测器。
创建一个新的 Python 文件并导入以下包:
from sklearn.datasets import fetch_20newsgroups from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer
定义将用于训练的类别图。 在这种情况下,我们将使用五个类别。 该字典对象中的键引用 scikit-learn 数据集中的名称:
# Define the category map category_map = {'talk.politics.misc': 'Politics', 'rec.autos': 'Autos', 'rec.sport.hockey': 'Hockey', 'sci.electronics': 'Electronics', 'sci.med': 'Medicine'}
使用fetch_20newsgroups
获取训练数据集:
# Get the training dataset training_data = fetch_20newsgroups(subset='train', categories=category_map.keys(), shuffle=True, random_state=5)
使用CountVectorizer
对象提取单词计数:
# Build a count vectorizer and extract term counts count_vectorizer = CountVectorizer() train_tc = count_vectorizer.fit_transform(training_data.data) print("\nDimensions of training data:", train_tc.shape)
创建 tf-idf 变压器,并使用以下数据对其进行训练:
# Create the tf-idf transformer tfidf = TfidfTransformer() train_tfidf = tfidf.fit_transform(train_tc)
定义一些将用于测试的样本输入语句:
# Define test data input_data = [ 'You need to be careful with cars when you are driving on slippery roads', 'A lot of devices can be operated wirelessly', 'Players need to be careful when they are close to goal posts', 'Political debates help us understand the perspectives of both sides' ]
使用训练数据训练多项式贝叶斯分类器:
# Train a Multinomial Naive Bayes classifier classifier = MultinomialNB().fit(train_tfidf, training_data.target)
使用计数向量化器转换输入数据:
# Transform input data using count vectorizer input_tc = count_vectorizer.transform(input_data)
使用tf-idf
转换器转换向量化的数据,以便可以在推理模型中运行它:
# Transform vectorized data using tfidf transformer input_tfidf = tfidf.transform(input_tc)
使用tf-idf
转换后的向量预测输出:
# Predict the output categories predictions = classifier.predict(input_tfidf)
在输入测试数据中打印每个样本的输出类别:
# Print the outputs for sent, category in zip(input_data, predictions): print('\nInput:', sent, '\nPredicted category:', \ category_map[training_data.target_names[category]])
完整代码在文件category_predictor.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HkUSeYS9-1681568737512)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_06.png)]
图 6:类别预测器输出
我们可以直观地看到预测的类别是正确的。 接下来,我们将看另一种形式的文本分析 – 性别识别。
构造性别标识符
性别识别是一个有趣的问题,远非一门精确的科学。 我们可以快速想到可以用于男性和女性的名字:
Dana
Angel
Lindsey
Morgan
Jessie
Chris
Payton
Tracy
Stacy
Jordan
Robin
Sydney
另外,在诸如美国的异质社会中,将会有许多种族名称不遵循英语规则。 通常,我们可以对各种名称进行有根据的猜测。 在这个简单的示例中,我们将使用启发式方法来构建特征向量,并使用它来训练分类器。 这里将使用的启发式是给定名称的最后N
个字母。 例如,如果名称以ia
结尾,则很可能是女性名称,例如Amelia
或Genelia
。 另一方面,如果名称以rk
结尾,则可能是男性名称,例如Mark
或Clark
。 由于我们不确定要使用的字母的确切数量,因此我们将使用此参数并找出最佳答案。 让我们来看看如何做。
创建一个新的 Python 文件,然后导入以下包:
import random from nltk import NaiveBayesClassifier from nltk.classify import accuracy as nltk_accuracy from nltk.corpus import names
定义一个函数以从输入单词中提取最后的N
个字母:
# Extract last N letters from the input word # and that will act as our "feature" def extract_features(word, N=2): last_n_letters = word[-N:] return {'feature': last_n_letters.lower()}
定义main
函数并从scikit-learn
包中提取训练数据。 此数据包含带有标签的男性和女性姓名:
if __name__=='__main__': # Create training data using labeled names available in NLTK male_list = [(name, 'male') for name in names.words('male.txt')] female_list = [(name, 'female') for name in names.words('female.txt')] data = (male_list + female_list)
播种随机数生成器并混洗数据:
# Seed the random number generator random.seed(5) # Shuffle the data random.shuffle(data)
创建一些用于测试的样本名称:
# Create test data input_names = ['Alexander', 'Danielle', 'David', 'Cheryl']
定义将用于训练和测试的数据百分比:
# Define the number of samples used for train and test num_train = int(0.8 * len(data))
最后的N
个字符将用作预测性别的特征向量。 将该参数更改为,以了解表现如何变化。 在这种情况下,我们将从1
转到6
:
# Iterate through different lengths to compare the accuracy for i in range(1, 6): print('\nNumber of end letters:', i) features = [(extract_features(n, i), gender) for (n, gender) in data]
将数据分为训练和测试:
train_data, test_data = features[:num_train], features[num_train:]
使用训练数据构建一个NaiveBayesClassifier
:
classifier = NaiveBayesClassifier.train(train_data)
使用 NLTK 中可用的内置精度方法来计算分类器的精度:
# Compute the accuracy of the classifier accuracy = round(100 * nltk_accuracy(classifier, test_data), 2) print('Accuracy = ' + str(accuracy) + '%')
预测输入测试列表中每个名称的输出:
# Predict outputs for input names using # the trained classifier model for name in input_names: print(name, '==>', classifier.classify(extract_features(name, i)))
完整代码在文件gender_identifier.py
中给出。 如果运行代码,您将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Con07c0F-1681568737512)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_07.png)]
图 7:性别识别输出
前面的屏幕截图显示了的准确率以及测试数据的预测输出。 让我们进一步看看发生了什么:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fruoq2W-1681568737512)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_08.png)]
图 8:性别识别输出
我们可以看到,准确率在两个字母处达到峰值,然后开始下降。 接下来,我们将研究另一个有趣的问题-肛门,它分析了文本的情感。
建立情感分析器
情感分析是确定一段文本情感的过程。 例如,它可以用于确定电影评论是正面的还是负面的。 这是自然语言处理最流行的应用之一。 我们还可以根据眼前的问题添加更多类别。 可以使用此技术来了解人们对产品,品牌或主题的感觉。 它通常用于分析营销活动,民意调查,社交媒体的存在,电子商务网站上的产品评论等。 让我们看看如何确定电影评论的情感。
我们将使用朴素的贝叶斯分类器来构建此情感分析器。 首先,从文本中提取所有唯一的单词。 NLTK 分类器需要将此数据以字典的形式排列,以便可以吸收它。 将文本数据分为训练和测试数据集后,将对朴素贝叶斯分类器进行训练,以将评论分为正面和负面。 然后,可以计算和显示表示正面和负面评论的最有用的信息。 该信息很有趣,因为它显示了正在使用的单词来表示各种反应。
让我们看看如何实现这一目标。 首先,创建一个新的 Python 文件并导入以下包:
from nltk.corpus import movie_reviews from nltk.classify import NaiveBayesClassifier from nltk.classify.util import accuracy as nltk_accuracy
定义一个函数,根据输入的单词构造一个字典对象并返回它:
# Extract features from the input list of words def extract_features(words): return dict([(word, True) for word in words])
定义main
函数并加载带有标签的电影评论:
if __name__=='__main__': # Load the reviews from the corpus fileids_pos = movie_reviews.fileids('pos') fileids_neg = movie_reviews.fileids('neg')
从电影评论中提取特征,并相应地标记它们:
# Extract the features from the reviews features_pos = [(extract_features(movie_reviews.words( fileids=[f])), 'Positive') for f in fileids_pos] features_neg = [(extract_features(movie_reviews.words( fileids=[f])), 'Negative') for f in fileids_neg]
定义训练和测试之间的区别。 在这种情况下,我们将为训练分配 80%,为测试分配 20%:
# Define the train and test split (80% and 20%) threshold = 0.8 num_pos = int(threshold * len(features_pos)) num_neg = int(threshold * len(features_neg))
分离用于训练和测试的特征向量:
# Create training and training datasets features_train = features_pos[:num_pos] + features_neg[:num_neg] features_test = features_pos[num_pos:] + features_neg[num_neg:]
打印用于训练和测试的数据点数:
# Print the number of datapoints used print('\nNumber of training datapoints:', len(features_train)) print('Number of test datapoints:', len(features_test))
使用训练数据训练NaiveBayesClassifier
,并使用 NLTK 中内置的精度方法来计算精度:
# Train a Naive Bayes classifier classifier = NaiveBayesClassifier.train(features_train) print('\nAccuracy of the classifier:', nltk_accuracy( classifier, features_test))
打印N
信息量最高的单词:
N = 15 print('\nTop ' + str(N) + ' most informative words:') for i, item in enumerate(classifier.most_informative_features()): print(str(i+1) + '. ' + item[0]) if i == N - 1: break
定义用于测试的样本句子:
# Test input movie reviews input_reviews = [ 'The costumes in this movie were great', 'I think the story was terrible and the characters were very weak', 'People say that the director of the movie is amazing', 'This is such an idiotic movie. I will not recommend it to anyone.' ]
遍历样本数据并预测输出:
print("\nMovie review predictions:") for review in input_reviews: print("\nReview:", review)
计算每个类别的概率:
# Compute the probabilities probabilities = classifier.prob_classify(extract_features(review.split()))
在概率中选择最大值:
# Pick the maximum value predicted_sentiment = probabilities.max()
打印预测的输出类别(正面或负面情感):
# Print outputs print("Predicted sentiment:", predicted_sentiment) print("Probability:", round(probabilities.prob(predicted_sentiment), 2))
完整代码在文件sentiment_analyzer.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNNyFw7u-1681568737512)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_09.png)]
图 9:情感分析输出
前面的屏幕截图显示了前 15 个最有用的单词。 如果向下滚动,您会看到 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B3T6YpHq-1681568737513)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_10.png)]
图 10:电影评论情感输出
我们可以直观地看到并验证预测是否正确。
在本节中,我们构建了一个复杂的情感分析器。 我们将继续在 NLP 空间中的旅程,并学习潜在狄利克雷分布的基础。
使用潜在狄利克雷分布的主题建模
主题建模是识别文本数据中与主题相对应的模式的过程。 如果文本包含多个主题,则可以使用此技术来识别和分隔输入文本中的那些主题。 该技术可用于发现给定文档集中的隐藏主题结构。
主题建模可以帮助我们以最佳方式组织文档,然后可以将其用于分析。 关于主题建模算法要注意的一件事是它们不需要标记的数据。 就像无监督学习一样,它将自行识别模式。 考虑到互联网上生成的大量文本数据,主题建模非常重要,因为它可以汇总大量数据,否则这是不可能的。
潜在狄利克雷分布是一种主题建模技术,其基本概念是文本的给定片段是多个主题的组合。 让我们考虑以下句子:数据可视化是财务分析中的重要工具。 这句话有多个主题,例如数据,可视化和财务。 这种组合有助于识别大型文档中的文本。 它是一个统计模型,试图捕获概念并基于这些概念创建模型。 该模型假定文档是基于这些主题通过随机过程生成的。 主题是固定单词词汇的分布。 让我们看看如何在 Python 中进行主题建模。
gensim
库将在本节中使用。 该库已在本章的第一部分中安装。 在继续操作之前,请确保已安装。 创建一个新的 Python 文件并导入以下包:
from nltk.tokenize import RegexpTokenizer from nltk.corpus import stopwords from nltk.stem.snowball import SnowballStemmer from gensim import models, corpora
定义一个函数来加载输入数据。 输入文件包含 10 个以行分隔的句子:
# Load input data def load_data(input_file): data = [] with open(input_file, 'r') as f: for line in f.readlines(): data.append(line[:-1]) return data
定义一个函数来处理输入文本。 第一步是将其标记化:
# Processor function for tokenizing, removing stop # words, and stemming def process(input_text): # Create a regular expression tokenizer tokenizer = RegexpTokenizer(r'\w+')
然后,我们需要阻止标记化文本:
# Create a Snowball stemmer stemmer = SnowballStemmer('english')
我们需要从输入文本中删除停用词,因为它们不会添加信息。 让我们获取停用词列表:
# Get the list of stop words stop_words = stopwords.words('english')
标记输入字符串:
# Tokenize the input string tokens = tokenizer.tokenize(input_text.lower())
删除停用词:
# Remove the stop words tokens = [x for x in tokens if not x in stop_words]
阻止标记化的单词并返回列表:
# Perform stemming on the tokenized words tokens_stemmed = [stemmer.stem(x) for x in tokens] return tokens_stemmed
定义main
函数并从为您提供的文件data.txt
中加载输入数据:
if __name__=='__main__': # Load input data data = load_data('data.txt')
标记文本:
# Create a list for sentence tokens tokens = [process(x) for x in data]
根据带标记的句子创建字典:
# Create a dictionary based on the sentence tokens dict_tokens = corpora.Dictionary(tokens)
使用句子标记创建文档项矩阵:
# Create a document-term matrix doc_term_mat = [dict_tokens.doc2bow(token) for token in tokens]
我们需要提供主题数作为输入参数。 在这种情况下,我们知道输入文本有两个不同的主题。 让我们指定:
# Define the number of topics for the LDA model num_topics = 2
生成LatentDirichlet
模型:
# Generate the LDA model ldamodel = models.ldamodel.LdaModel(doc_term_mat, num_topics=num_topics, id2word=dict_tokens, passes=25)
打印每个主题的前五个贡献词:
num_words = 5 print('\nTop ' + str(num_words) + ' contributing words to each topic:') for item in ldamodel.print_topics(num_topics=num_topics, num_words=num_words): print('\nTopic', item[0]) # Print the contributing words along with # their relative contributions list_of_strings = item[1].split(' + ') for text in list_of_strings: weight = text.split('*')[0] word = text.split('*')[1] print(word, '==>', str(round(float(weight) * 100, 2)) + '%')
完整代码在文件topic_modeler.py
中给出。 如果运行代码,将得到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6rQS3wU-1681568737513)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/ai-py/img/B15441_15_11.png)]
图 11:主题建模器输出
我们可以看到,将数学和历史这两个主题区分开来是相当不错的。 如果您查看这些文本,则可以验证每个句子都是关于数学还是历史的。
总结
在本章中,我们学习了自然语言处理中的各种基础概念。 我们讨论了标记化以及如何将输入文本分成多个标记。 我们学习了如何使用词干和词形还原将单词简化为基本形式。 我们实现了文本分块器,可根据预定义的条件将输入文本分为多个块。
我们讨论了词袋模型,并为输入文本建立了文档项矩阵。 然后,我们学习了如何使用机器学习对文本进行分类。 我们使用启发式方法构造了性别标识符。 我们还使用机器学习来分析电影评论的情感。 最后,我们讨论了主题建模并实现了一个用于识别给定文档中主题的系统。
在下一章中,我们将学习如何使用隐马尔可夫模型对序列数据进行建模,然后使用它们来分析股市数据。