Python-机器学习秘籍第二版-三-

54 阅读58分钟

Python 机器学习秘籍第二版(三)

原文:annas-archive.org/md5/1a114450f966ee5154c07d3ee2c9ce43

译者:飞龙

协议:CC BY-NC-SA 4.0

第五章:可视化数据

在本章中,我们将介绍以下食谱:

  • 绘制三维散点图

  • 绘制气泡图

  • 动画气泡图

  • 绘制饼图

  • 绘制日期格式的时间序列数据

  • 绘制直方图

  • 可视化热图

  • 动态信号动画

  • 使用 Seaborn 库进行操作

技术要求

为了处理本章中的食谱,您将需要以下文件(可在 GitHub 上找到):

  • scatter_3d.py

  • bubble_plot.py

  • dynamic_bubble_plot.py

  • pie_chart.py

  • time_series.py

  • aapl.csv

  • histogram.py

  • heatmap.py

  • moving_wave_variable.py

  • seaborn.boxplot.py

数据可视化简介

数据可视化是机器学习的一个重要支柱。它帮助我们制定正确的策略来理解数据。数据的视觉表示有助于我们选择正确的算法。数据可视化的一个主要目标是使用图表和图表清晰地传达信息。

在现实世界中,我们经常遇到数值数据。我们希望通过图表、线条、点、条形等来编码这些数值数据,以直观地显示这些数字中包含的信息。这使得复杂的数据分布更容易理解和使用。这个过程在各种情况下都得到应用,包括比较分析、跟踪增长、市场分布、民意调查等等。

我们使用不同的图表来展示变量之间的模式或关系。我们使用直方图来显示数据的分布。当我们想要查找特定的测量值时,我们使用表格。在本章中,我们将探讨各种场景并讨论在这些情况下我们可以使用哪些可视化。

绘制三维散点图

可以使用散点图来表示定量变量之间的关系。这种图表的一个版本是三维散点图,用于展示三个变量之间的关系。

准备工作

在本食谱中,您将学习如何绘制三维散点图并在三维中可视化它们。

如何做到...

让我们看看如何绘制三维散点图:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已提供的 scatter_3d.py 文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 创建空图,如下所示:
# Create the figure 
fig = plt.figure() 
ax = fig.add_subplot(111, projection='3d') 
  1. 定义我们应该生成的值的数量:
# Define the number of values 
n = 250 
  1. 创建一个 lambda 函数来生成给定范围内的值:
# Create a lambda function to generate the random values in the given range 
f = lambda minval, maxval, n: minval + (maxval - minval) * np.random.rand(n) 
  1. 使用 lambda 函数生成 x、y 和 z 值:
# Generate the values 
x_vals = f(15, 41, n) y_vals = f(-10, 70, n) 
z_vals = f(-52, -37, n) 
  1. 按照以下方式绘制这些值:
# Plot the values 
ax.scatter(x_vals, y_vals, z_vals, c='k', marker='o') 
ax.set_xlabel('X axis') 
ax.set_ylabel('Y axis') 
ax.set_zlabel('Z axis') 

plt.show() 

如果您运行前面的代码,您将看到以下输出:

图片

它是如何工作的...

散点图帮助我们理解两个定量特征之间是否存在统计关联。如果一个变量增加,另一个变量倾向于减少,我们有一个不一致的关联。如果一个变量增加,另一个变量也倾向于增加,我们有一个一致的关联。如果一个变量变化,而另一个变量倾向于不变化,我们没有任何关联。为了分析这种趋势,分析标记的位置。如果标记在图形的三维空间中的任何方向上接近形成一条直线,则对应变量的相关性很高。如果标记在图中均匀分布,则相关性低,或为零。

更多...

三维散点图用于显示三个变量之间的关系。可以通过匹配标记的颜色或大小添加第四个变量,将另一个变量添加到图中。

参见

绘制气泡图

气泡图是一种图表,其中每个表示的实体由三个不同的数值参数定义。前两个参数用作两个笛卡尔轴的值,而第三个参数用于确定气泡的半径。气泡图用于描述各个科学领域的关联关系。

准备工作

让我们看看如何绘制气泡图。在二维气泡图中,每个圆的大小代表该特定点的振幅。

如何做到这一点...

让我们看看如何绘制气泡图:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在您已经提供的bubble_plot.py文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 定义我们应该生成的值的数量:
# Define the number of values 
num_vals = 40 
  1. xy生成随机值:
# Generate random values 
x = np.random.rand(num_vals) 
y = np.random.rand(num_vals) 
  1. 定义气泡图中每个点的面积值:
# Define area for each bubble 
# Max radius is set to a specified value 
max_radius = 25 
area = np.pi * (max_radius * np.random.rand(num_vals)) ** 2   
  1. 定义颜色:
# Generate colors 
colors = np.random.rand(num_vals) 
  1. 绘制这些值:
# Plot the points 
plt.scatter(x, y, s=area, c=colors, alpha=1.0) 

plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

之前显示的变量可以根据它们的大小以及它们相对于数值轴的位置进行比较。实际上,气泡图的x轴和y轴是数值尺度,因此数据表示的位置描述了两个数值,而图形的面积取决于第三个参数的值。

在绘制气泡图时,需要注意以下事实:圆的面积与半径的平方成正比,因此如果半径与第三个值成比例,结果将不成比例地强调第三个值。为了有一个正确权衡的刻度,半径必须与第三个值的平方根成比例。在绘制气泡图时,这种错误是常见的。

更多...

气泡图可以被视为散点图的一种变体,其中点被气泡所取代。如果数据有三个系列,每个系列包含一组数据,则可以使用此类图表代替散点图。

参见

动画气泡图

动画气泡图是一种动态的气泡图。它允许通过时间和交互式地高效可视化相关性。它特别有用,因为它允许我们有效地和交互式地可视化变量之间的相关性如何随时间变化。

准备工作

让我们看看如何动画气泡图。当您想要可视化瞬态和动态数据时,这将很有用。

如何做到这一点...

让我们看看如何动画气泡图:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的dynamic_bubble_plot.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.animation import FuncAnimation  
  1. 让我们定义一个tracker函数,该函数将动态更新气泡图:
def tracker(cur_num): 
    # Get the current index  
    cur_index = cur_num % num_points 
  1. 定义颜色:
    # Set the color of the datapoints  
    datapoints['color'][:, 3] = 1.0 
  1. 更新圆的大小:
    # Update the size of the circles  
    datapoints['size'] += datapoints['growth'] 
  1. 更新集合中最旧数据点的位置:
    # Update the position of the oldest datapoint  
    datapoints['position'][cur_index] = np.random.uniform(0, 1, 2) 
    datapoints['size'][cur_index] = 7 
    datapoints['color'][cur_index] = (0, 0, 0, 1) 
    datapoints['growth'][cur_index] = np.random.uniform(40, 150) 
  1. 更新散点图的参数:
    # Update the parameters of the scatter plot  
    scatter_plot.set_edgecolors(datapoints['color']) 
    scatter_plot.set_sizes(datapoints['size']) 
    scatter_plot.set_offsets(datapoints['position']) 
  1. 定义main函数并创建一个空图:
if __name__=='__main__': 
    # Create a figure  
    fig = plt.figure(figsize=(9, 7), facecolor=(0,0.9,0.9)) 
    ax = fig.add_axes([0, 0, 1, 1], frameon=False) 
    ax.set_xlim(0, 1), ax.set_xticks([]) 
    ax.set_ylim(0, 1), ax.set_yticks([]) 
  1. 定义在任何给定时间点图上将会有的点的数量:
    # Create and initialize the datapoints in random positions  
    # and with random growth rates. 
    num_points = 20 
  1. 使用随机值定义datapoints
    datapoints = np.zeros(num_points, dtype=[('position', float, 2), 
            ('size', float, 1), ('growth', float, 1), ('color', float, 4)]) 
    datapoints['position'] = np.random.uniform(0, 1, (num_points, 2)) 
    datapoints['growth'] = np.random.uniform(40, 150, num_points) 
  1. 创建将在每一帧更新的散点图:
    # Construct the scatter plot that will be updated every frame 
    scatter_plot = ax.scatter(datapoints['position'][:, 0], datapoints['position'][:, 1], 
                      s=datapoints['size'], lw=0.7, edgecolors=datapoints['color'], 
                      facecolors='none') 
  1. 通过使用tracker函数开始动画:
    # Start the animation using the 'tracker' function  
    animation = FuncAnimation(fig, tracker, interval=10) 

    plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

在这个菜谱中,我们简单地使用了一系列随时间变化的气泡图,通过构建一个更新这些图参数的函数来构建动画。为此,我们首先构建了一个更新这些图参数的函数。然后,我们定义了跟踪当前参数的气泡图的代码。最后,我们使用FuncAnimation()函数从单个气泡图中创建动画。

更多...

Matplotlib 的FuncAnimation()函数通过重复调用一个特定的函数来创建动画。

参见

绘制饼图

圆形图,通常被称为饼图,是描述性统计中用于图形表示在类别(名义值)类别上测量的定量变量的方法,以避免无意中建立不存在的类别顺序。

准备工作

让我们看看如何绘制饼图。这在你想可视化一组标签的百分比时非常有用。

如何做到这一点...

让我们看看如何绘制饼图,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的pie_chart.py文件中):
import matplotlib.pyplot as plt  
  1. 定义标签和值:
# Labels and corresponding values in counter clockwise direction 
data = {'Apple': 26,  
        'Mango': 17, 
        'Pineapple': 21,  
        'Banana': 29,  
        'Strawberry': 11} 
  1. 定义用于可视化的颜色:
# List of corresponding colors 
colors = ['orange', 'lightgreen', 'lightblue', 'gold', 'cyan'] 
  1. 定义一个变量来突出显示饼图的一部分,使其与其他部分分离。如果你不想突出任何部分,将所有值设置为0
# Needed if we want to highlight a section 
explode = (0, 0, 0, 0, 0)   
  1. 绘制饼图。请注意,如果你使用的是 Python 3,你应该在以下函数调用中使用list(data.values())
# Plot the pie chart 
plt.pie(data.values(), explode=explode, labels=data.keys(),  
        colors=colors, autopct='%1.1f%%', shadow=False, startangle=90) 

# Aspect ratio of the pie chart, 'equal' indicates tht we  
# want it to be a circle 
plt.axis('equal') 

plt.show() 

如果你运行此代码,你将看到以下输出:

图片

  1. 如果你将 explode 数组更改为(0, 0.2, 0, 0, 0),它将突出显示芒果部分。你将看到以下输出:

图片

它是如何工作的...

饼图是通过将圆分成角度幅度与频率类别成比例的切片来构建的。切片所标识的面积与频率成比例。为了使图表更清晰,不同的切片被填充了不同的颜色。

还有更多...

饼图用于显示产品和品牌的市场份额,或政治党派在选举中所占的百分比。当百分比差异很大或元素太多时,图表将无法正常工作,因为这会使饼图过于参差不齐。

参见

绘制日期格式的时间序列数据

时间序列是一系列现象的观察结果,在连续的瞬间或时间间隔内进行。通常,即使不是必要的,它们也是均匀分布的或长度相同。商品价格趋势、股票市场指数、政府债券收益率和失业率只是时间序列的几个例子。

准备工作

让我们看看如何使用日期格式绘制时间序列数据。这将在可视化随时间变化的股票数据时很有用。

如何做...

让我们看看如何绘制日期格式化的时间序列数据,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的time_series.py文件中):
import numpy 
import matplotlib.pyplot as plt 
from matplotlib.mlab import csv2rec 
from matplotlib.ticker import Formatter 
  1. 定义一个用于格式化日期的函数。__init__函数设置了类变量:
# Define a class for formatting 
class DataFormatter(Formatter): 
    def __init__(self, dates, date_format='%Y-%m-%d'): 
        self.dates = dates 
        self.date_format = date_format 
  1. 在任何给定时间提取值,并以下列格式返回:
    # Extract the value at time t at position 'position' 
    def __call__(self, t, position=0): 
        index = int(round(t)) 
        if index >= len(self.dates) or index < 0: 
            return '' 

        return self.dates[index].strftime(self.date_format) 
  1. 定义main函数。我们将使用您已经提供的苹果股票报价 CSV 文件(aapl.csv)。加载 CSV 文件:
    # Load csv file into numpy record array 
    data = csv2rec('aapl.csv') 
  1. 从这些值中提取一个子集以进行绘图:
    # Take a subset for plotting 
    data = data[-70:] 
  1. 创建formatter对象并用日期初始化它:
    # Create the date formatter object 
    formatter = DataFormatter(data.date) 
  1. 定义xy轴:
    # X axis 
    x_vals = numpy.arange(len(data)) 

    # Y axis values are the closing stock quotes 
    y_vals = data.close  
  1. 绘制数据:
    # Plot data 
    fig, ax = plt.subplots() 
    ax.xaxis.set_major_formatter(formatter) 
    ax.plot(x_vals, y_vals, 'o-') 
    fig.autofmt_xdate() 
    plt.show() 

如果你运行此代码,你将看到以下输出:

它是如何工作的...

在这个菜谱中,我们使用日期格式绘制了时间序列数据。我们首先定义了一个用于格式化日期的类。我们在特定位置提取了时间t的值。然后,我们将 CSV 文件加载到 NumPy 记录数组中。因此,我们提取了用于绘图的子集,并创建了日期formatter对象。最后,我们设置了xy轴并绘制了数据。

还有更多...

可视化是时间序列分析的基础。查看原始数据可以提供识别时间结构的工具,例如趋势、周期和季节性。然而,日期的格式化代表了一个你必须学习的操作,以便获得正确的轴可视化。

参考信息

绘制直方图

直方图是数值分布的再现,显示了分布的形状。它由相邻的矩形(箱)组成,其底边与轴对齐,并配备了度量单位。

更多信息请参考 Giuseppe Ciaburro 所著的《MATLAB for Machine Learning》一书。

准备工作

我们将在这个菜谱中查看如何绘制直方图。我们将比较两组数据并构建一个比较直方图。

如何做...

让我们看看如何绘制直方图,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的histogram.py文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 在这个菜谱中,我们将比较苹果和橙子的产量。让我们定义一些值:
# Input data 
apples = [30, 25, 22, 36, 21, 29] 
oranges = [24, 33, 19, 27, 35, 20] 

# Number of groups 
num_groups = len(apples) 
  1. 创建图形并定义其参数:
# Create the figure 
fig, ax = plt.subplots() 

# Define the X axis 
indices = np.arange(num_groups) 

# Width and opacity of histogram bars 
bar_width = 0.4 
opacity = 0.6 
  1. 绘制直方图:
# Plot the values 
hist_apples = plt.bar(indices, apples, bar_width,  
        alpha=opacity, color='g', label='Apples') 

hist_oranges = plt.bar(indices + bar_width, oranges, bar_width, 
        alpha=opacity, color='b', label='Oranges') 
  1. 设置绘图参数:
plt.xlabel('Month') 
plt.ylabel('Production quantity') 
plt.title('Comparing apples and oranges') 
plt.xticks(indices + bar_width, ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun')) 
plt.ylim([0, 45]) 
plt.legend() 
plt.tight_layout() 

plt.show() 

如果你运行此代码,你将看到以下输出:

它是如何工作的...

直方图是一种特殊的笛卡尔图,横轴上有离散值,纵轴上有大小,通过柱子的高度表示,我们称之为箱。在物理学中,直方图使我们能够研究实验的结果,因为它们为我们提供了图形指示,说明了计数或频率是如何根据考虑的离散值分布的。

要构建直方图,请执行以下步骤:

  1. 首先,你必须决定测试的数量。

  2. 然后,你必须选择分箱,即在横轴上变量域的区间划分。因此,你必须定义区间的数量,其中第j个箱将具有预定的宽度。

  3. 最后,你必须计算出现次数以与个别箱关联。

更多内容...

在以下情况下,直方图最有用:

  • 要表示的数据是数值类型。

  • 你想要可视化数据分布的形式,以便查看它是否为正态分布,并分析一个过程是否可以(或不能)满足所施加的要求。

  • 你想要确定两个或更多过程输出是否不同。

  • 你想要快速传达数据分布。

  • 你想要检查在某个时间段内过程是否发生了变化。

参考信息

可视化热图

热图是一种图表,其中矩阵中包含的个别值通过颜色的渐变来表示。分形图和树形图通常使用相同的颜色编码系统来表示变量的层次。例如,如果我们测量网页上的点击次数或鼠标指针最常经过的区域,我们将获得一个以暖色突出显示的特定区域的热图,即那些最能吸引我们注意力的区域。

准备工作

我们将探讨如何在这个配方中可视化热图。这是一个数据点的图示表示,其中两个组是点对点关联的。矩阵中包含的个别值在图中表示为颜色值。

如何做...

让我们看看如何可视化热图:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的heatmap.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
  1. 定义两组:
# Define the two groups  
group1 = ['France', 'Italy', 'Spain', 'Portugal', 'Germany']  
group2 = ['Japan', 'China', 'Brazil', 'Russia', 'Australia'] 
  1. 生成一个随机的二维矩阵:
# Generate some random values 
data = np.random.rand(5, 5) 
  1. 创建一个图形:
# Create a figure 
fig, ax = plt.subplots() 
  1. 创建热图:
# Create the heat map 
heatmap = ax.pcolor(data, cmap=plt.cm.gray) 
  1. 绘制这些值:
# Add major ticks at the middle of each cell 
ax.set_xticks(np.arange(data.shape[0]) + 0.5, minor=False) 
ax.set_yticks(np.arange(data.shape[1]) + 0.5, minor=False) 

# Make it look like a table  
ax.invert_yaxis() 
ax.xaxis.tick_top() 

# Add tick labels 
ax.set_xticklabels(group2, minor=False) 
ax.set_yticklabels(group1, minor=False) 

plt.show() 

如果你运行此代码,你将看到以下输出:

图片

它是如何工作的...

我们已经说过,热图是一种数据图形表示类型,其中值通过不同的颜色范围来表示。其表示通常包括以下内容:

  • 暖色:红色、橙色和黄色,用于最感兴趣的区域

  • 冷色:绿色或蓝色,用于表现较差的区域

通常,热图需要大量的数据样本,它们的主要目的是获取关于特定变量趋势的有用数据。这种分析方法使我们能够评估分析区域内感兴趣变量的分布。这样,特定区域暖色浓度的集中将突出变量取最高值的地方。

还有更多...

热图的创造者是 Cormac Kinney,他在 20 世纪 90 年代中期开发了这个解决方案,为股市操作员提供了一个收集各种金融数据的极快工具。

参见

动画动态信号

当我们可视化实时信号时,看看波形是如何建立起来的是很不错的。一个动态系统是一个数学模型,它代表了一个具有有限自由度的对象,该对象根据确定性法则随时间演变。动态系统由相空间中的一个向量来识别,即系统状态的空间,其中状态是一个术语,表示一组物理量,称为状态变量,它们表征了系统的动力学。

准备工作

在这个菜谱中,我们将探讨如何动画化动态信号,并在实时遇到时可视化它们。

如何做到...

让我们看看如何动画化动态信号,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的moving_wave_variable.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.animation as animation  
  1. 创建一个生成阻尼正弦波信号的函数:
# Generate the signal 
def generate_data(length=2500, t=0, step_size=0.05): 
    for count in range(length): 
        t += step_size 
        signal = np.sin(2*np.pi*t) 
        damper = np.exp(-t/8.0) 
        yield t, signal * damper  
  1. 定义一个initializer函数来初始化绘图参数:
# Initializer function 
def initializer(): 
    peak_val = 1.0 
    buffer_val = 0.1 
  1. 按照以下方式设置这些参数:
    ax.set_ylim(-peak_val * (1 + buffer_val), peak_val * (1 + buffer_val)) 
    ax.set_xlim(0, 10) 
    del x_vals[:] 
    del y_vals[:] 
    line.set_data(x_vals, y_vals) 
    return line 
  1. 定义一个函数来绘制值:
def draw(data): 
    # update the data 
    t, signal = data 
    x_vals.append(t) 
    y_vals.append(signal) 
    x_min, x_max = ax.get_xlim() 
  1. 如果值超出了当前的x轴限制,那么更新并扩展图表:
    if t >= x_max: 
        ax.set_xlim(x_min, 2 * x_max) 
        ax.figure.canvas.draw() 

    line.set_data(x_vals, y_vals) 

    return line 
  1. 定义main函数:
if __name__=='__main__': 
    # Create the figure 
    fig, ax = plt.subplots() 
    ax.grid() 
  1. 提取以下行:
    # Extract the line 
    line, = ax.plot([], [], lw=1.5) 
  1. 创建变量并将它们初始化为空列表:
    # Create the variables 
    x_vals, y_vals = [], [] 
  1. 使用animator对象定义并开始动画:
    # Define the animator object 
    animator = animation.FuncAnimation(fig, draw, generate_data,  
            blit=False, interval=10, repeat=False, init_func=initializer) 

    plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

在这个菜谱中,我们动画化了动态信号,并将它们实时可视化。为此,我们使用了阻尼正弦波。当我们荡秋千时,我们知道在初始推动后,它会振荡一段时间然后停止。秋千是一个摆的例子,其运动由阻尼正弦波表示。阻尼正弦波是一个振幅随时间增加而趋近于零的正弦函数。

更多内容...

正弦波用于描述许多振荡现象。当正弦波被抑制时,每个后续峰值随时间减小。最常见的阻尼形式是指数阻尼,其中后续峰值的包络是指数衰减曲线。

参见

使用 Seaborn 库

箱线图,也称为须状图,是一种使用简单分散度和位置指数来描述样本分布的图表。箱线图可以水平或垂直绘制,通过两个段落的矩形分区来表示。矩形(箱)由第一个四分位数(25%分位数)和第三个四分位数(75%分位数)界定,并通过中位数(50%分位数)分割。

准备工作

在这个菜谱中,我们将绘制箱线图来展示波士顿数据集中包含的预测变量的分布,我们在第一章的监督学习领域中使用的估算房价菜谱中已经使用过这些数据,链接。

如何操作...

让我们看看如何使用seaborn库,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已提供的seaborn.boxplot.py文件中):
import pandas as pd
from sklearn import datasets
import seaborn as sns  
  1. 加载sklearn.datasets库中包含的数据集:
boston = datasets.load_boston()
  1. 将数据转换为 pandas DataFrame
BostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)
  1. 提取前12个特征作为预测变量:
    Predictors = BostonDF[BostonDF.columns[0:12]]
  1. 使用seaborn库绘制箱线图:
sns.set(style="ticks")
sns.boxplot(data = Predictors)

让我们查看以下输出中的结果:

我们可以看到预测变量有不同的值范围。这使得图表难以阅读;某些预测变量的变异性没有得到突出显示。在这些情况下,有必要对数据进行缩放。

  1. 导入sklearn.preprocessing.MinMaxScaler库并缩放数据,如下所示:
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
DataScaled = scaler.fit_transform(Predictors)
  1. 现在,我们再次绘制箱线图以查看差异:
sns.set(style="ticks")
sns.boxplot(data = DataScaled)

结果如下截图所示:

在这种情况下,我们可以看到数据在 0-1 之间。这样,所有预测变量的数据变异性都是可识别的。

它是如何工作的...

Seaborn 库是一个基于 Matplotlib 的数据可视化库。它适用于绘制吸引人且信息丰富的统计图形,并且与 Pandas 数据结构紧密集成。Seaborn 提供了许多可以帮助我们进行数据源视觉分析的功能。

这些功能可以在seaborn.pydata.org/introduction.html中查看。

更多内容...

在箱线图中,箱体外的部分(即须须)代表样本的最小值和最大值。通过这种方式,四个等量的范围,由四分位数界定,被图形化地表示出来。

参见

第六章:构建推荐引擎

在本章中,我们将介绍以下食谱:

  • 构建数据处理的功能组合

  • 构建机器学习管道

  • 寻找最近邻

  • 构建 k 最近邻分类器

  • 构建 k 最近邻回归器

  • 计算欧几里得距离得分

  • 计算皮尔逊相关得分

  • 在数据集中寻找相似用户

  • 生成电影推荐

  • 实现排名算法

  • 使用 TensorFlow 构建过滤模型

技术要求

为了处理本章中的食谱,你需要以下文件(这些文件可在 GitHub 上找到):

  • function_composition.py

  • pipeline.py

  • knn.py

  • nn_classification.py

  • nn_regression.py

  • euclidean_score.py

  • pearson_score.py

  • find_similar_users.py

  • movie_recommendations.py

  • LambdaMARTModel.py

  • train.txt

  • vali.txt

  • test.txt

  • TensorFilter.py

介绍推荐引擎

推荐引擎是一个可以预测用户可能感兴趣的内容的模型。当我们将其应用于电影等场景时,这变成了电影推荐引擎。我们通过预测当前用户可能会如何评分来过滤数据库中的项目。这有助于我们将用户与数据集中的正确内容相连接。这有什么相关性?如果你有一个庞大的目录,那么用户可能或可能找不到所有与他们相关的所有内容。通过推荐正确的内容,你可以增加消费。像 Netflix 这样的公司严重依赖推荐来保持用户的参与度。

推荐引擎通常使用协同过滤或基于内容的过滤来生成一组推荐。两种方法之间的区别在于推荐挖掘的方式。协同过滤从当前用户的过去行为以及其他用户的评分构建模型。然后我们使用这个模型来预测这个用户可能会感兴趣的内容。另一方面,基于内容的过滤使用项目本身的特征来向用户推荐更多项目。项目之间的相似性是这里的驱动力。在本章中,我们将重点介绍协同过滤。

构建数据处理的功能组合

任何机器学习系统的主要部分之一是数据处理管道。在数据被输入到机器学习算法进行训练之前,我们需要以不同的方式对其进行处理,使其适合该算法。拥有一个健壮的数据处理管道对于构建准确和可扩展的机器学习系统至关重要。有很多基本功能可用,数据处理管道通常是由这些功能的组合构成的。与其以嵌套或循环的方式调用这些函数,不如使用函数式编程范式来构建组合。

准备工作

让我们看看如何将这些基本函数组合成一个可重用的函数组合。在这个菜谱中,我们将创建三个基本函数,并查看如何构建一个管道。

如何做...

让我们看看如何构建数据处理的功能组合:

  1. 创建一个新的 Python 文件,并添加以下行(完整的代码在提供的function_composition.py文件中):
import numpy as np
from functools import reduce 
  1. 让我们定义一个函数,将3加到数组的每个元素上:
def add3(input_array): 
    return map(lambda x: x+3, input_array) 
  1. 现在,让我们定义第二个函数,将2乘以数组的每个元素:
def mul2(input_array): return map(lambda x: x*2, input_array) 
  1. 现在,让我们再定义一个函数,从数组的每个元素中减去5
def sub5(input_array): 
    return map(lambda x: x-5, input_array)
  1. 让我们定义一个函数组合器,它接受函数作为输入参数,并返回一个组合函数。这个组合函数基本上是一个按顺序应用所有输入函数的函数:
def function_composer(*args): 
    return reduce(lambda f, g: lambda x: f(g(x)), args) 

我们使用reduce函数通过依次应用序列中的函数来组合所有输入函数。

  1. 现在,我们已经准备好使用这个函数组合器了。让我们定义一些数据和一系列操作:
if __name__=='__main__': 
    arr = np.array([2,5,4,7]) 

    print("Operation: add3(mul2(sub5(arr)))") 
  1. 如果我们使用常规方法,我们会依次应用,如下所示:
    arr1 = add3(arr) 
    arr2 = mul2(arr1) 
    arr3 = sub5(arr2) 
    print("Output using the lengthy way:", list(arr3)) 
  1. 现在,让我们使用函数组合器在单行中实现相同的功能:
    func_composed = function_composer(sub5, mul2, add3) 
    print("Output using function composition:", list(func_composed(arr)))  
  1. 我们也可以用前一种方法在单行中做同样的事情,但符号会变得非常嵌套和难以阅读。此外,它不可重用;如果你想重用这个操作序列,你必须再次写下整个内容:
    print("Operation: sub5(add3(mul2(sub5(mul2(arr)))))\nOutput:", \
            list(function_composer(mul2, sub5, mul2, add3, sub5)(arr)))
  1. 如果你运行此代码,你将在终端上得到以下输出:
Operation: add3(mul2(sub5(arr)))
Output using the lengthy way: [5, 11, 9, 15]
Output using function composition: [5, 11, 9, 15]
Operation: sub5(add3(mul2(sub5(mul2(arr)))))
Output: [-10, 2, -2, 10]

它是如何工作的...

在这个菜谱中,我们创建了三个基本函数,并学习了如何组合一个管道。为此,我们使用了reduce()函数。这个函数接受一个函数和一个序列,并返回一个单一值。

reduce()函数计算返回值,如下所示:

  • 首先,函数通过使用序列的前两个元素来计算结果。

  • 接下来,函数使用上一步得到的结果和序列中的下一个值。

  • 这个过程会一直重复,直到序列的末尾。

还有更多...

在菜谱开头使用的三个基本函数利用了map()函数。这个函数用于将一个函数应用于特定值的所有元素。结果返回一个 map 对象;这个对象是一个迭代器,因此我们可以遍历其元素。为了打印这个对象,我们将 map 对象转换为序列对象,即列表。

参见

构建机器学习管道

scikit-learn库用于构建机器学习管道。当我们定义函数时,库将构建一个组合对象,使数据通过整个管道。这个管道可以包括函数,如预处理、特征选择、监督学习和无监督学习。

准备工作

在这个菜谱中,我们将构建一个管道,用于获取输入特征向量,选择前* k* 个特征,然后使用随机森林分类器进行分类。

如何做到这一点...

让我们看看如何构建机器学习管道:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 pipeline.py文件中):
from sklearn.datasets import samples_generator 
from sklearn.ensemble import RandomForestClassifier 
from sklearn.feature_selection import SelectKBest, f_regression 
from sklearn.pipeline import Pipeline 
  1. 让我们生成一些样本数据来玩耍,如下所示:
# generate sample data 
X, y = samples_generator.make_classification( 
        n_informative=4, n_features=20, n_redundant=0, random_state=5) 

这行生成了 20 维的特征向量,因为这是默认值。您可以使用上一行中的 n_features参数来更改它。

  1. 我们管道的第一步是在数据点进一步使用之前选择* k* 个最佳特征。在这种情况下,让我们将 k 设置为 10
# Feature selector  
selector_k_best = SelectKBest(f_regression, k=10)
  1. 下一步是使用随机森林分类器方法对数据进行分类:
# Random forest classifier 
classifier = RandomForestClassifier(n_estimators=50, max_depth=4)
  1. 现在,我们已经准备好构建管道。 Pipeline()方法允许我们使用预定义的对象来构建管道:
# Build the machine learning pipeline 
pipeline_classifier = Pipeline([('selector', selector_k_best), ('rf', classifier)]) 

我们还可以为管道中的块分配名称。在上行中,我们将 selector 名称分配给我们的特征选择器,将 rf分配给我们的随机森林分类器。您可以使用任何其他随机名称!

  1. 我们还可以在过程中更新这些参数。我们可以使用之前步骤中分配的名称来设置参数。例如,如果我们想在特征选择器中将 k 设置为 6,在随机森林分类器中将 n_estimators设置为 25,我们可以像以下代码所示那样做。请注意,这些是之前步骤中给出的变量名称:
pipeline_classifier.set_params(selector__k=6,  
        rf__n_estimators=25) 
  1. 让我们继续训练分类器:
# Training the classifier 
pipeline_classifier.fit(X, y) 
  1. 现在让我们预测训练数据的输出,如下所示:
# Predict the output prediction = pipeline_classifier.predict(X) print("Predictions:\n", prediction) 
  1. 现在,让我们估计这个分类器的性能,如下所示:
# Print score 
print("Score:", pipeline_classifier.score(X, y))                         
  1. 我们还可以查看哪些特征将被选择,所以让我们继续并打印它们:
# Print the selected features chosen by the selector features_status = pipeline_classifier.named_steps['selector'].get_support() selected_features = [] for count, item in enumerate(features_status): if item: selected_features.append(count) print("Selected features (0-indexed):", ', '.join([str(x) for x in selected_features]))
  1. 如果您运行此代码,您将在您的终端上得到以下输出:
Predictions:
 [1 1 0 1 0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 1 0 0 1
 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 0 0 1 1 1 0 0 1 0 0 0 1 1 0 0 1
 1 1 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 1 1 1 0 1 1 0 1]
Score: 0.95
Selected features (0-indexed): 0, 5, 9, 10, 11, 15

它是如何工作的...

选择* k* 个最佳特征的优势在于我们将能够处理低维数据。这有助于减少计算复杂性。我们选择* k* 个最佳特征的方式基于单变量特征选择。这执行单变量统计测试,然后从特征向量中提取表现最好的特征。单变量统计测试是指只涉及一个变量的分析技术。

更多内容...

一旦进行这些测试,特征向量中的每个特征都会被分配一个分数。基于这些分数,我们选择前k个特征。我们将此作为分类器管道中的预处理步骤。一旦我们提取了前k个特征,就形成了一个 k 维特征向量,我们将其用作随机森林分类器的输入训练数据。

参考信息

寻找最近邻

最近邻模型指的是一类旨在根据训练数据集中最近邻的数量做出决策的算法。最近邻方法包括找到与新的点距离相近的预定义数量的训练样本,并预测标签。样本的数量可以是用户定义的、一致的或不同的,这取决于点的局部密度。距离可以用任何度量标准来计算——标准欧几里得距离是最常见的选项。基于邻居的方法只是简单地记住所有训练数据。

准备工作

在本菜谱中,我们将使用笛卡尔平面上的一系列点来寻找最近邻。

如何做到这一点...

让我们看看如何找到最近邻,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码已包含在您提供的knn.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.neighbors import NearestNeighbors 
  1. 让我们创建一些二维样本数据:
# Input data 
X = np.array([[1, 1], [1, 3], [2, 2], [2.5, 5], [3, 1],  
        [4, 2], [2, 3.5], [3, 3], [3.5, 4]])
  1. 我们的目标是找到任何给定点的三个最近邻,所以让我们定义这个参数:
# Number of neighbors we want to find 
num_neighbors = 3 
  1. 让我们定义一个不在输入数据中的随机数据点:
# Input point 
input_point = [2.6, 1.7] 
  1. 我们需要看看这些数据看起来像什么;让我们按如下方式绘制它:
# Plot datapoints 
plt.figure() 
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k') 
  1. 为了找到最近邻,我们需要定义具有正确参数的NearestNeighbors对象,并在输入数据上对其进行训练:
# Build nearest neighbors model 
knn = NearestNeighbors(n_neighbors=num_neighbors, algorithm='ball_tree').fit(X) 
  1. 我们现在可以找到输入点到输入数据中所有点的distances参数:
distances, indices = knn.kneighbors(input_point) 
  1. 我们可以打印k 最近邻,如下所示:
# Print the 'k' nearest neighbors 
print("k nearest neighbors")
for rank, index in enumerate(indices[0][:num_neighbors]):
    print(str(rank+1) + " -->", X[index])

indices数组已经排序,所以我们只需要解析它并打印数据点。

  1. 现在,让我们绘制输入数据点和突出显示 k 个最近邻:
# Plot the nearest neighbors  
plt.figure() 
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k') 
plt.scatter(X[indices][0][:][:,0], X[indices][0][:][:,1],  
        marker='o', s=150, color='k', facecolors='none') 
plt.scatter(input_point[0], input_point[1], 
        marker='x', s=150, color='k', facecolors='none') 

plt.show() 
  1. 如果你运行此代码,你将在你的终端上得到以下输出:
k nearest neighbors
1 --> [2\. 2.]
2 --> [3\. 1.]
3 --> [3\. 3.]

这是输入数据点的图示:

第二个输出图显示了测试数据点的位置和三个最近邻,如下面的截图所示:

它是如何工作的...

在这个菜谱中,我们通过使用笛卡尔平面上的点来寻找最近邻。为此,根据训练对象的位置和特征将空间划分为区域。即使它不是初始条件所明确要求的,这也可以被认为是算法的训练集。为了计算距离,对象通过多维空间中的位置向量来表示。最后,如果一个点是最接近被考察对象的 k 个例子中最频繁的,则将其分配到某个类别。邻近性是通过点之间的距离来衡量的。邻居是从一组已知正确分类的对象中选取的。

更多内容...

为了构建最近邻模型,使用了BallTree算法。BallTree是一种在多维空间中组织点的数据结构。该算法之所以得名,是因为它将数据点划分为一系列嵌套的超球体,称为球体。它在许多应用中很有用,尤其是最近邻搜索。

参考以下内容

构建 k 最近邻分类器

k 最近邻算法是一种使用训练数据集中的 k 个最近邻来寻找未知对象类别的算法。当我们想要找到未知点所属的类别时,我们找到 k 个最近邻并进行多数投票。

准备工作

在这个菜谱中,我们将从包含一系列在笛卡尔平面上排列的点输入数据开始创建一个 k 最近邻分类器,这些点显示了三个区域内的分组。

如何做到这一点...

让我们看看如何构建 k-最近邻分类器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的nn_classification.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.cm as cm 
from sklearn import neighbors, datasets 

from utilities import load_data 
  1. 我们将使用data_nn_classifier.txt文件作为输入数据。让我们加载这个输入数据:
# Load input data 
input_file = 'data_nn_classifier.txt' 
data = load_data(input_file) 
X, y = data[:,:-1], data[:,-1].astype(np.int) 

前两列包含输入数据,最后一列包含标签。因此,我们将它们分离成Xy,如前述代码所示。

  1. 现在,让我们可视化输入数据,如下所示:
# Plot input data 
plt.figure() 
plt.title('Input datapoints') 
markers = '^sov<>hp' 
mapper = np.array([markers[i] for i in y]) 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

我们遍历所有数据点,并使用适当的标记来区分类别。

  1. 为了构建分类器,我们需要指定我们想要考虑的最近邻数量。让我们定义这个参数:
# Number of nearest neighbors to consider 
num_neighbors = 10
  1. 为了可视化边界,我们需要定义一个网格并在该网格上评估分类器。让我们定义步长:
# step size of the grid 
h = 0.01   
  1. 我们现在准备好构建 k-最近邻分类器。让我们定义它并训练它,如下所示:
# Create a K-Neighbours Classifier model and train it 
classifier = neighbors.KNeighborsClassifier(num_neighbors, weights='distance') 
classifier.fit(X, y)
  1. 我们需要创建一个网格来绘制边界。让我们定义如下:
# Create the mesh to plot the boundaries 
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 
x_grid, y_grid = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) 
  1. 现在,让我们评估所有点的classifier输出:
# Compute the outputs for all the points on the mesh 
predicted_values = classifier.predict(np.c_[x_grid.ravel(), y_grid.ravel()]) 
  1. 让我们绘制它,如下所示:
# Put the computed results on the map 
predicted_values = predicted_values.reshape(x_grid.shape) 
plt.figure() 
plt.pcolormesh(x_grid, y_grid, predicted_values, cmap=cm.Pastel1) 
  1. 现在我们已经绘制了颜色网格,让我们叠加训练数据点以查看它们相对于边界的位置:
# Overlay the training points on the map 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.xlim(x_grid.min(), x_grid.max()) 
plt.ylim(y_grid.min(), y_grid.max()) 
plt.title('k nearest neighbors classifier boundaries')
  1. 现在,我们可以考虑一个测试数据点并查看分类器是否表现正确。让我们定义它并绘制它,如下所示:
# Test input datapoint 
test_datapoint = [4.5, 3.6] 
plt.figure() 
plt.title('Test datapoint') 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',  
        linewidth=3, s=200, facecolors='black') 
  1. 我们需要使用以下模型提取 k-最近邻分类器:
# Extract k nearest neighbors 
dist, indices = classifier.kneighbors(test_datapoint) 
  1. 让我们绘制 k-最近邻分类器并突出显示它:
# Plot k nearest neighbors 
plt.figure() 
plt.title('k nearest neighbors') 

for i in indices: 
    plt.scatter(X[i, 0], X[i, 1], marker='o',  
            linewidth=3, s=100, facecolors='black') 

plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',  
        linewidth=3, s=200, facecolors='black') 

for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.show() 
  1. 现在,让我们在终端上打印classifier输出:
print("Predicted output:", classifier.predict(test_datapoint)[0])

打印出以下结果:

Predicted output: 2

此外,展示了一系列图表。第一个输出图表描述了输入数据点的分布:

图片

第二个输出图表描述了使用k-最近邻分类器获得的边界:

图片

第三个输出图表描述了测试数据点的位置:

图片

第四个输出图表描述了 10 个最近邻的位置:

图片

它是如何工作的...

k-最近邻分类器存储所有可用的数据点,并根据相似性度量对新数据点进行分类。这个相似性度量通常以距离函数的形式出现。这个算法是一种非参数技术,这意味着在制定之前不需要找出任何潜在参数。我们只需要选择一个对我们来说合适的k值。

一旦我们找到了 k 近邻分类器,我们就进行多数投票。新的数据点通过 k 近邻分类器的多数投票进行分类。这个数据点被分配给其 k 个最近邻中最常见的类别。如果我们把k的值设为1,那么这仅仅是一个最近邻分类器的案例,我们只需将数据点分配给训练数据集中其最近邻的类别。

更多内容...

k 近邻算法基于通过考虑训练集最接近的 k 个样本的类别来对未知样本进行分类的概念。新的样本将被分配给大多数 k 个最近样本所属的类别。因此,k 的选择对于将样本分配到正确的类别非常重要。如果 k 太小,分类可能对噪声敏感;如果 k 太大,分类可能计算成本高,并且邻域可能包括属于其他类别的样本。

参考以下内容

构建一个 k 近邻回归器

我们学习了如何使用 k 近邻算法构建分类器。好事是,我们也可以将此算法用作回归器。对象的输出由其属性值表示,这是其 k 个最近邻的值的平均值。

准备工作

在这个菜谱中,我们将看到如何使用 k 近邻算法构建回归器。

如何操作...

让我们看看如何构建一个 k 近邻回归器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的nn_regression.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn import neighbors 
  1. 让我们生成一些样本高斯分布数据:
# Generate sample data 
amplitude = 10 
num_points = 100 
X = amplitude * np.random.rand(num_points, 1) - 0.5 * amplitude 
  1. 我们需要向数据中添加一些噪声,以引入一些随机性。添加噪声的目的是看看我们的算法是否能够克服它,并且仍然以鲁棒的方式运行:
# Compute target and add noise 
y = np.sinc(X).ravel()  
y += 0.2 * (0.5 - np.random.rand(y.size))
  1. 现在,让我们可视化它,如下所示:
# Plot input data 
plt.figure() 
plt.scatter(X, y, s=40, c='k', facecolors='none') 
plt.title('Input data') 
  1. 我们刚刚生成了一些数据,并在所有这些点上评估了一个连续值函数。让我们定义一个更密集的点网格:
# Create the 1D grid with 10 times the density of the input data 
x_values = np.linspace(-0.5*amplitude, 0.5*amplitude, 10*num_points)[:, np.newaxis] 

我们定义这个更密集的网格是因为我们想要评估我们的回归器在这些所有点上,并查看它如何近似我们的函数。

  1. 现在我们定义我们想要考虑的最近邻的数量:
# Number of neighbors to consider  
n_neighbors = 8 
  1. 让我们使用之前定义的参数初始化并训练 k 近邻回归器:
# Define and train the regressor 
knn_regressor = neighbors.KNeighborsRegressor(n_neighbors, weights='distance') 
y_values = knn_regressor.fit(X, y).predict(x_values) 
  1. 让我们通过将输入和输出数据重叠在一起来查看回归器的表现:
plt.figure() 
plt.scatter(X, y, s=40, c='k', facecolors='none', label='input data') 
plt.plot(x_values, y_values, c='k', linestyle='--', label='predicted values') 
plt.xlim(X.min() - 1, X.max() + 1) 
plt.ylim(y.min() - 0.2, y.max() + 0.2) 
plt.axis('tight') 
plt.legend() 
plt.title('K Nearest Neighbors Regressor') 

plt.show()
  1. 如果你运行此代码,第一个图表描述了输入数据点:

图片

第二个图表展示了回归器预测的值:

图片

它是如何工作的...

回归器的目标是预测连续值输出。在这种情况下,我们没有固定数量的输出类别。我们只有一组实值输出值,我们希望我们的回归器预测未知数据点的输出值。在这种情况下,我们使用 sinc 函数来演示 k 近邻回归器。这也被称为 卡丹尔正弦函数sinc 函数由以下方程定义:

图片

x0 时,sin(x)/x 取得不定形 0/0。因此,我们必须计算当 x 趋近于 0 时该函数的极限。我们使用一组值进行训练,并为测试定义了一个更密集的网格。如图所示,输出曲线接近训练输出。

还有更多...

这种方法的主要优点是它不需要学习或构建模型;它可以以任意方式调整其决策边界,产生最灵活的模型表示;并且它还保证了增加训练集的可能性。然而,此算法也有许多缺点,包括易受数据噪声的影响、对无关特征的敏感,以及需要相似度度量来评估邻近性。

参考信息

计算欧几里得距离得分

现在我们已经对机器学习管道和最近邻分类器有了足够的背景知识,让我们开始讨论推荐引擎。为了构建推荐引擎,我们需要定义一个相似性度量,这样我们就可以找到与给定用户相似的数据库中的用户。欧几里得距离得分就是这样一种度量,我们可以用它来计算数据点之间的距离。我们将讨论转向电影推荐引擎。

准备工作

在这个菜谱中,我们将了解如何计算两个用户之间的欧几里得得分。

如何操作...

让我们看看如何计算欧几里得距离得分:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经为你提供的euclidean_score.py文件中):
import json 
import numpy as np 
  1. 我们现在将定义一个函数来计算两个用户之间的欧几里得得分。第一步是检查用户是否存在于数据库中:
# Returns the Euclidean distance score between user1 and user2  
def euclidean_score(dataset, user1, user2): 
    if user1 not in dataset: 
        raise TypeError('User ' + user1 + ' not present in the dataset') 

    if user2 not in dataset: 
        raise TypeError('User ' + user2 + ' not present in the dataset') 
  1. 为了计算得分,我们需要提取两个用户都评价过的电影:
    # Movies rated by both user1 and user2 
    rated_by_both = {}  

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            rated_by_both[item] = 1 
  1. 如果没有共同的电影,那么用户之间没有相似性(或者至少,根据数据库中的评分我们无法计算它):
    # If there are no common movies, the score is 0  
    if len(rated_by_both) == 0: 
        return 0 
  1. 对于每个共同评分,我们只计算平方差的和的平方根并归一化,使得得分在 0 到 1 之间:
    squared_differences = []  

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            squared_differences.append(np.square(dataset[user1][item] - dataset[user2][item])) 

    return 1 / (1 + np.sqrt(np.sum(squared_differences)))  

如果评分相似,则平方差的和将非常低。因此,得分将变得很高,这正是我们想要的这个度量标准。

  1. 我们将使用movie_ratings.json文件作为我们的数据文件。让我们按照以下方式加载它:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 让我们考虑两个随机用户并计算欧几里得距离得分:
    user1 = 'John Carson' 
    user2 = 'Michelle Peterson' 

    print("Euclidean score:")
    print(euclidean_score(data, user1, user2)) 
  1. 当你运行此代码时,你将在终端上看到以下欧几里得距离得分:
0.29429805508554946

工作原理...

在大多数情况下,最近邻算法中使用的距离定义为两个点之间的欧几里得距离

根据以下公式计算点数:

在二维平面上,欧几里得距离表示两点之间的最小距离,因此是连接两点的直线。这个距离是两个向量元素平方差的和的平方根,如前一个公式所示。

还有更多...

有其他类型的度量用于计算距离。所有这些类型通常试图避免平方根,因为它们在计算上很昂贵,并且是几个错误的原因。度量包括闵可夫斯基曼哈顿余弦距离。

参考信息

计算皮尔逊相关系数

欧几里得距离得分是一个好的度量标准,但它有一些缺点。因此,皮尔逊相关系数在推荐引擎中经常被使用。两个统计变量之间的皮尔逊相关系数是一个指数,表示它们之间可能存在的线性关系。它衡量两个数值变量同时变化的趋势。

准备工作

在这个菜谱中,我们将看到如何计算皮尔逊相关系数。

如何做...

让我们看看如何计算皮尔逊相关系数:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在为你提供的pearson_score.py文件中):
import json 
import numpy as np 
  1. 我们将定义一个函数来计算数据库中两个用户之间的皮尔逊相关系数。我们的第一步是确认这些用户存在于数据库中:
# Returns the Pearson correlation score between user1 and user2  
def pearson_score(dataset, user1, user2): 
    if user1 not in dataset: 
        raise TypeError('User ' + user1 + ' not present in the dataset') 

    if user2 not in dataset: 
        raise TypeError('User ' + user2 + ' not present in the dataset') 
  1. 下一步是获取这两位用户都评分的电影:
    # Movies rated by both user1 and user2 
    rated_by_both = {} 

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            rated_by_both[item] = 1 

    num_ratings = len(rated_by_both)  
  1. 如果没有共同的电影,那么这些用户之间没有可识别的相似性;因此,我们返回0
    # If there are no common movies, the score is 0  
    if num_ratings == 0: 
        return 0
  1. 我们需要计算共同电影评分的平方值之和:
    # Compute the sum of ratings of all the common preferences  
    user1_sum = np.sum([dataset[user1][item] for item in rated_by_both]) 
    user2_sum = np.sum([dataset[user2][item] for item in rated_by_both]) 
  1. 现在,让我们计算所有共同电影评分的平方值之和:
    # Compute the sum of squared ratings of all the common preferences  
    user1_squared_sum = np.sum([np.square(dataset[user1][item]) for item in rated_by_both]) 
    user2_squared_sum = np.sum([np.square(dataset[user2][item]) for item in rated_by_both]) 
  1. 现在让我们计算乘积之和:
    # Compute the sum of products of the common ratings  
    product_sum = np.sum([dataset[user1][item] * dataset[user2][item] for item in rated_by_both]) 
  1. 我们现在准备计算计算皮尔逊相关系数所需的各种元素:
    # Compute the Pearson correlation 
    Sxy = product_sum - (user1_sum * user2_sum / num_ratings) 
    Sxx = user1_squared_sum - np.square(user1_sum) / num_ratings 
    Syy = user2_squared_sum - np.square(user2_sum) / num_ratings 
  1. 我们需要处理分母变为0的情况:
    if Sxx * Syy == 0: 
        return 0 
  1. 如果一切正常,我们返回皮尔逊相关系数,如下所示:
    return Sxy / np.sqrt(Sxx * Syy) 
  1. 现在让我们定义main函数并计算两个用户之间的皮尔逊相关系数:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 

    user1 = 'John Carson' 
    user2 = 'Michelle Peterson' 

    print("Pearson score:")
    print(pearson_score(data, user1, user2)) 
  1. 如果你运行此代码,你将在终端上看到以下皮尔逊相关系数:
Pearson score:
0.39605901719066977

它是如何工作的...

皮尔逊相关系数的r系数衡量的是变量在间隔或等效比率之间的相关性。它由两个变量的标准化分数的乘积之和(z[x] * z[y])除以受试者(或观察)的数量得出,如下所示:

这个系数可以取介于-1.00(两个变量之间存在完美的负相关)和+1.00(两个变量之间存在完美的正相关)之间的值。相关性为 0 表示两个变量之间没有关系。

还有更多...

必须记住,皮尔逊公式与线性关系相关,因此,所有不同形式的关系都可能产生异常结果。

参考资料还包括

在数据集中找到相似的用户

在构建推荐引擎时,最重要的任务之一是找到相似的用户。这在创建提供给这些用户的推荐时非常有用。

准备工作

在这个菜谱中,我们将看到如何构建一个模型来查找相似用户。

如何操作...

让我们看看如何在数据集中查找相似用户:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的 find_similar_users.py 文件中):
import json 
import numpy as np 

from pearson_score import pearson_score 
  1. 让我们定义一个函数来查找与输入用户相似的用户。它接受三个输入参数:数据库、输入用户以及我们正在寻找的相似用户数量。我们的第一步是检查用户是否存在于数据库中。如果用户存在,我们需要计算该用户与数据库中所有其他用户的皮尔逊相关得分:
# Finds a specified number of users who are similar to the input user 
def find_similar_users(dataset, user, num_users): 
    if user not in dataset: 
        raise TypeError('User ' + user + ' not present in the dataset') 

    # Compute Pearson scores for all the users 
    scores = np.array([[x, pearson_score(dataset, user, x)] for x in dataset if user != x]) 
  1. 下一步是将这些分数按降序排列:
    # Sort the scores based on second column 
    scores_sorted = np.argsort(scores[:, 1]) 

    # Sort the scores in decreasing order (highest score first)  
    scored_sorted_dec = scores_sorted[::-1] 
  1. 现在我们提取 k 个最高得分并返回它们:
    # Extract top 'k' indices 
    top_k = scored_sorted_dec[0:num_users]  

    return scores[top_k]  
  1. 现在我们定义主函数并加载输入数据库:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 我们想找到与 John Carson 相似的三个用户。我们通过以下步骤来完成:
    user = 'John Carson'
    print("Users similar to " + user + ":\n")
    similar_users = find_similar_users(data, user, 3) 
    print("User\t\t\tSimilarity score\n")
    for item in similar_users:
        print(item[0], '\t\t', round(float(item[1]), 2))
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Users similar to John Carson:

User               Similarity score
Michael Henry                  0.99
Alex Roberts                   0.75
Melissa Jones                  0.59

它是如何工作的...

在这个菜谱中,我们正在寻找与输入用户相似的用户。给定数据库、输入用户以及我们正在寻找的相似用户数量,我们首先检查用户是否存在于数据库中。如果用户存在,计算该用户与数据库中所有其他用户的皮尔逊相关得分。

还有更多...

为了计算皮尔逊相关得分,使用了 pearson_score() 函数。这个函数在之前的 计算皮尔逊相关得分 菜谱中定义。

相关内容

生成电影推荐

在这个菜谱中,我们将生成电影推荐。

准备工作

在这个菜谱中,我们将使用之前菜谱中构建的所有功能来构建一个电影推荐引擎。让我们看看如何构建它。

如何操作...

让我们看看如何生成电影推荐:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的 movie_recommendations.py 文件中):
import json 
import numpy as np 

from pearson_score import pearson_score 
  1. 我们将定义一个函数来为给定用户生成电影推荐。第一步是检查该用户是否存在于数据集中:
# Generate recommendations for a given user 
def generate_recommendations(dataset, user): 
    if user not in dataset: 
        raise TypeError('User ' + user + ' not present in the dataset') 
  1. 现在我们来计算该用户与数据集中所有其他用户的皮尔逊得分:
    total_scores = {} 
    similarity_sums = {} 

    for u in [x for x in dataset if x != user]: 
        similarity_score = pearson_score(dataset, user, u) 

        if similarity_score <= 0: 
            continue 
  1. 我们需要找到该用户尚未评分的电影:
        for item in [x for x in dataset[u] if x not in dataset[user] or dataset[user][x] == 0]: 
            total_scores.update({item: dataset[u][item] * similarity_score}) 
            similarity_sums.update({item: similarity_score}) 
  1. 如果用户观看了数据库中的每一部电影,那么我们无法为该用户推荐任何内容。让我们处理这个条件:
    if len(total_scores) == 0: 
        return ['No recommendations possible'] 
  1. 现在我们有了这些分数的列表。让我们创建一个电影排名的归一化列表:
    # Create the normalized list 
    movie_ranks = np.array([[total/similarity_sums[item], item]  
            for item, total in total_scores.items()]) 
  1. 我们需要根据分数按降序对列表进行排序:
    # Sort in decreasing order based on the first column 
    movie_ranks = movie_ranks[np.argsort(movie_ranks[:, 0])[::-1]] 
  1. 我们终于准备好提取电影推荐了:
    # Extract the recommended movies 
    recommendations = [movie for _, movie in movie_ranks] 

    return recommendations
  1. 现在,让我们定义main函数并加载数据集:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 现在,让我们为Michael Henry生成推荐,如下所示:
    user = 'Michael Henry'
    print("Recommendations for " + user + ":")
    movies = generate_recommendations(data, user) 
    for i, movie in enumerate(movies):
        print(str(i+1) + '. ' + movie)
  1. John Carson用户观看了所有电影。因此,如果我们尝试为他生成推荐,应该显示 0 个推荐。让我们看看这是否会发生,如下所示:
    user = 'John Carson' 
    print("Recommendations for " + user + ":")
    movies = generate_recommendations(data, user) 
    for i, movie in enumerate(movies):
        print(str(i+1) + '. ' + movie)
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Recommendations for Michael Henry:
1\. Jerry Maguire
2\. Inception
3\. Anger Management
Recommendations for John Carson:
1\. No recommendations possible

它是如何工作的...

在这个菜谱中,我们已经构建了一个电影推荐引擎。要为特定用户生成推荐,将执行以下步骤:

  1. 首先,我们检查用户是否存在于数据库中

  2. 然后,我们计算皮尔逊相关系数

  3. 然后,我们创建归一化列表

  4. 然后,我们根据第一列按降序排序这个列表

  5. 最后,我们提取推荐的电影

更多内容...

要构建电影推荐引擎,使用了pearson_score()函数。该函数在之前的计算皮尔逊相关系数评分菜谱中定义。

参考信息

实现排名算法

学习排名(LTR)是一种用于构建信息检索系统分类模型的方法。训练数据由包含诱导部分顺序的文章列表组成,该顺序为每篇文章提供一个数值或序数评分,或一个二元判断。模型的目的是根据从文章中获得的判断来考虑的评分,将元素重新排序到新的列表中。

准备工作

在这个菜谱中,我们将使用pyltr包,这是一个 Python LTR 工具包,包含排名模型、评估指标和数据整理助手。

如何做...

让我们看看如何实现排名算法:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的LambdaMARTModel.py文件中):
import pyltr
  1. 我们将加载 Letor 数据集中包含的数据(train.txtvali.txttest.txt):
with open('train.txt') as trainfile, \
        open('vali.txt') as valifile, \
        open('test.txt') as testfile:
    TrainX, Trainy, Trainqids, _ = pyltr.data.letor.read_dataset(trainfile)
    ValX, Valy, Valqids, _ = pyltr.data.letor.read_dataset(valifile)
    TestX, Testy, Testqids, _ = pyltr.data.letor.read_dataset(testfile)
    metric = pyltr.metrics.NDCG(k=10)
  1. 现在我们对数据进行验证:
    monitor = pyltr.models.monitors.ValidationMonitor(
                ValX, Valy, Valqids, metric=metric, stop_after=250)
  1. 我们将按照以下步骤构建模型:
 model = pyltr.models.LambdaMART(
    metric=metric,
    n_estimators=1000,
    learning_rate=0.02,
    max_features=0.5,
    query_subsample=0.5,
    max_leaf_nodes=10,
    min_samples_leaf=64,
    verbose=1,
)
  1. 现在,我们可以使用文本数据来拟合模型:
model.fit(TestX, Testy, Testqids, monitor=monitor)
  1. 接下来,我们可以按照以下方式预测数据:
Testpred = model.predict(TestX)
  1. 最后,我们按照以下方式打印结果:
print('Random ranking:', metric.calc_mean_random(Testqids, Testy))
print('Our model:', metric.calc_mean(Testqids, Testy, Testpred))

以下结果将被打印:

Early termination at iteration 480
Random ranking: 0.27258472902087394
Our model: 0.5487673789992693

它是如何工作的...

LambdaMART 是 LambdaRank 的增强树版本,而 LambdaRank 又是基于 RankNet 的。RankNet、LambdaRank 和 LambdaMART 是用于解决许多上下文中的分类问题的算法。RankNet、LambdaRank 和 LambdaMART 是由微软研究小组的 Chris Burges 及其团队开发的。RankNet 是第一个被开发的,随后是 LambdaRank,然后是 LambdaMART。

RankNet 基于神经网络的使用,但其底层模型并不仅限于神经网络。RankNet 的成本函数旨在最小化排序中的反转次数。RankNet 使用随机梯度下降来优化成本函数。

研究人员发现,在 RankNet 训练过程中,不需要成本,只需要与模型分数相比的成本梯度(λ)。你可以将这些梯度想象成附加在分类列表中每个文档上的小箭头,指示我们可以移动这些文档的方向。LambdaRank 基于这个假设。

还有更多...

最后,LambdaMART 结合了 LambdaRank 中的方法和多重回归加性树MART)中的方法。虽然 MART 使用增强梯度的决策树进行预测,但 LambdaMART 使用来自 LambdaRank 的成本函数的增强梯度决策树来解决排序任务。LambdaMART 证明比 LambdaRank 和原始 RankNet 更有效。

参考信息

使用 TensorFlow 构建过滤模型

协同过滤是指一类工具和机制,它允许从大量未区分的知识中检索有关给定用户兴趣的预测信息。协同过滤在推荐系统环境中被广泛使用。协同算法的一个知名类别是矩阵分解。

协同过滤概念背后的基本假设是,每个表现出一定偏好集合的用户将继续在未来表现出这些偏好。协同过滤的一个流行例子是从给定用户的基本口味和偏好知识开始的电影推荐系统。应注意的是,尽管这些信息是针对单个用户的,但他们是从整个用户系统中处理过的知识中推导出来的。

准备工作

在这个菜谱中,我们将看到如何使用 TensorFlow 构建用于个性化推荐的协同过滤模型。我们将使用 MovieLens 1M 数据集,该数据集包含大约 6,000 名用户对大约 4,000 部电影的大约 100 万条评分。

如何做到这一点...

让我们看看如何使用 TensorFlow 构建过滤模型:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的TensorFilter.py文件中):
import numpy as np
import pandas as pd
import tensorflow as tf
  1. 我们将加载您已经提供的 MovieLens 1M 数据集中包含的数据(ratings.csv):
Data = pd.read_csv('ratings.csv', sep=';', names=['user', 'item', 'rating', 'timestamp'], header=None)

Data = Data.iloc[:,0:3]

NumItems = Data.item.nunique() 
NumUsers = Data.user.nunique()

print('Item: ', NumItems)
print('Users: ', NumUsers)

以下返回结果:

Item: 3706
Users: 6040
  1. 现在,让我们进行数据缩放,如下所示:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
Data['rating'] = Data['rating'].values.astype(float)
DataScaled = pd.DataFrame(scaler.fit_transform(Data['rating'].values.reshape(-1,1)))
Data['rating'] = DataScaled
  1. 我们将构建用户物品矩阵,如下所示:
UserItemMatrix = Data.pivot(index='user', columns='item', values='rating')
UserItemMatrix.fillna(0, inplace=True)

Users = UserItemMatrix.index.tolist()
Items = UserItemMatrix.columns.tolist()

UserItemMatrix = UserItemMatrix.as_matrix()
  1. 现在,我们可以设置一些网络参数,如下所示:
NumInput = NumItems
NumHidden1 = 10
NumHidden2 = 5
  1. 现在,我们将初始化 TensorFlow 占位符。然后,权重偏置被随机初始化:
X = tf.placeholder(tf.float64, [None, NumInput])

weights = {
    'EncoderH1': tf.Variable(tf.random_normal([NumInput, NumHidden1], dtype=tf.float64)),
    'EncoderH2': tf.Variable(tf.random_normal([NumHidden1, NumHidden2], dtype=tf.float64)),
    'DecoderH1': tf.Variable(tf.random_normal([NumHidden2, NumHidden1], dtype=tf.float64)),
    'DecoderH2': tf.Variable(tf.random_normal([NumHidden1, NumInput], dtype=tf.float64)),
}

biases = {
    'EncoderB1': tf.Variable(tf.random_normal([NumHidden1], dtype=tf.float64)),
    'EncoderB2': tf.Variable(tf.random_normal([NumHidden2], dtype=tf.float64)),
    'DecoderB1': tf.Variable(tf.random_normal([NumHidden1], dtype=tf.float64)),
    'DecoderB2': tf.Variable(tf.random_normal([NumInput], dtype=tf.float64)),
}
  1. 现在,我们可以构建编码器和解码器模型,如下所示:
def encoder(x):
    Layer1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['EncoderH1']), biases['EncoderB1']))
    Layer2 = tf.nn.sigmoid(tf.add(tf.matmul(Layer1, weights['EncoderH2']), biases['EncoderB2']))
    return Layer2

def decoder(x):
    Layer1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['DecoderH1']), biases['DecoderB1']))
    Layer2 = tf.nn.sigmoid(tf.add(tf.matmul(Layer1, weights['DecoderH2']), biases['DecoderB2']))
    return Layer2
  1. 我们将构建模型并预测值,如下所示:
EncoderOp = encoder(X)
DecoderOp = decoder(EncoderOp)

YPred = DecoderOp

YTrue = X
  1. 现在,我们将定义损失优化器,并最小化平方误差和评估指标:
loss = tf.losses.mean_squared_error(YTrue, YPred)
Optimizer = tf.train.RMSPropOptimizer(0.03).minimize(loss)
EvalX = tf.placeholder(tf.int32, )
EvalY = tf.placeholder(tf.int32, )
Pre, PreOp = tf.metrics.precision(labels=EvalX, predictions=EvalY)
  1. 让我们现在初始化变量,如下所示:
Init = tf.global_variables_initializer()
LocalInit = tf.local_variables_initializer()
PredData = pd.DataFrame()
  1. 最后,我们可以开始训练我们的模型:
with tf.Session() as session:
    Epochs = 120
    BatchSize = 200

    session.run(Init)
    session.run(LocalInit)

    NumBatches = int(UserItemMatrix.shape[0] / BatchSize)
    UserItemMatrix = np.array_split(UserItemMatrix, NumBatches)

    for i in range(Epochs):

        AvgCost = 0

        for batch in UserItemMatrix:
            _, l = session.run([Optimizer, loss], feed_dict={X: batch})
            AvgCost += l

        AvgCost /= NumBatches

        print("Epoch: {} Loss: {}".format(i + 1, AvgCost))

    UserItemMatrix = np.concatenate(UserItemMatrix, axis=0)

    Preds = session.run(DecoderOp, feed_dict={X: UserItemMatrix})

    PredData = PredData.append(pd.DataFrame(Preds))

    PredData = PredData.stack().reset_index(name='rating')
    PredData.columns = ['user', 'item', 'rating']
    PredData['user'] = PredData['user'].map(lambda value: Users[value])
    PredData['item'] = PredData['item'].map(lambda value: Items[value])

    keys = ['user', 'item']
    Index1 = PredData.set_index(keys).index
    Index2 = Data.set_index(keys).index

    TopTenRanked = PredData[~Index1.isin(Index2)]
    TopTenRanked = TopTenRanked.sort_values(['user', 'rating'], ascending=[True, False])
    TopTenRanked = TopTenRanked.groupby('user').head(10)

    print(TopTenRanked.head(n=10))

以下结果返回:


 user item rating
2651 1 2858 0.295800
1106 1 1196 0.278715
1120 1 1210 0.251717
2203 1 2396 0.227491
1108 1 1198 0.213989
579  1 593  0.201507
802  1 858  0.196411
2374 1 2571 0.195712
309  1 318  0.191919
2785 1 2997 0.188679

这是针对1个用户的 top 10 结果。

它是如何工作的...

协同过滤方法侧重于寻找对相同对象做出相似判断的用户,从而在用户之间建立联系,向他们推荐两个用户中任何一个都正面评价或与之互动的对象。这样,我们寻找的是用户之间的关联,而不是对象之间的关联。

还有更多...

用户物品矩阵代表用户对某个对象的偏好,但如果按列读取,则突出显示某个电影是被喜欢还是不喜欢的。这样,你可以看到两个对象之间的相似性也可以通过观察喜欢相同电影的同一群人,以某种方式相似地表达出来。

相关内容

第七章:分析文本数据

在本章中,我们将涵盖以下食谱:

  • 使用标记化进行数据预处理

  • 对文本数据进行词干提取

  • 使用词形还原将文本转换为基本形式

  • 使用分块划分文本

  • 构建词袋模型

  • 构建文本分类器

  • 识别名字的性别

  • 分析句子的情感

  • 使用主题建模在文本中识别模式

  • 使用 spaCy 进行词性标注

  • 使用 gensim 的 Word2Vec

  • 使用浅层学习进行垃圾邮件检测

技术要求

为了完成本章的食谱,您需要以下文件(可在 GitHub 上找到):

  • tokenizer.py

  • stemmer.py

  • lemmatizer.py

  • chunking.py

  • bag_of_words.py

  • tfidf.py

  • `gender_identification.py`

  • sentiment_analysis.py

  • topic_modeling.py

  • data_topic_modeling.txt

  • PosTagging.py

  • GensimWord2Vec.py

  • LogiTextClassifier.py

  • spam.csv

简介

文本分析和自然语言处理(NLP)是现代人工智能系统的一个基本组成部分。计算机擅长理解结构严格、变化有限的数据。然而,当我们处理非结构化、自由形式的文本时,事情开始变得困难。开发 NLP 应用具有挑战性,因为计算机很难理解其背后的概念。我们的交流方式也有很多微妙的变体,这些变体可能以方言、上下文、俚语等形式存在。

为了解决这个问题,基于机器学习的 NLP 应用被开发出来。这些算法在文本数据中检测模式,以便我们可以从中提取见解。人工智能公司大量使用 NLP 和文本分析来提供相关结果。NLP 最常见的一些应用包括搜索引擎、情感分析、主题建模、词性标注和实体识别。NLP 的目标是开发一套算法,以便我们可以用普通的英语与计算机交互。如果我们能实现这一点,那么我们就无需编程语言来指导计算机应该做什么。在本章中,我们将探讨一些专注于文本分析和如何从文本数据中提取有意义信息的食谱。

在本章中,我们将大量使用名为自然语言工具包(NLTK)的 Python 包。在继续之前,请确保您已经安装了它:

  • 您可以在www.nltk.org/install.html找到安装步骤。

  • 您还需要安装NLTK Data,它包含许多语料库和训练模型。这是文本分析的一个基本组成部分!您可以在www.nltk.org/data.html找到安装步骤。

使用标记化进行数据预处理

分词是将文本分割成一系列有意义的片段的过程。这些片段被称为标记。例如,我们可以将一大段文本分割成单词,或者将其分割成句子。根据手头的任务,我们可以定义自己的条件将输入文本分割成有意义的标记。让我们看看如何做到这一点。

准备工作

分词是文本计算分析的第一步,涉及将字符序列分割成称为标记的最小分析单位。标记包括各种文本部分类别(单词、标点、数字等),也可以是复杂单位(如日期)。在这个菜谱中,我们将展示如何将一个复杂的句子分割成许多标记。

如何做到这一点...

让我们看看如何使用分词进行数据预处理:

  1. 创建一个新的 Python 文件并添加以下行(完整的代码在已经提供给你的tokenizer.py文件中)。让我们导入包和语料库:
import nltk
nltk.download('punkt')
  1. 让我们定义一些用于分析的样本text
text = "Are you curious about tokenization? Let's see how it works! We need to analyze a couple of sentences with punctuations to see it in action." 
  1. 让我们从句子分词开始。NLTK 提供了一个句子标记器,所以让我们导入它:
# Sentence tokenization 
from nltk.tokenize import sent_tokenize 
  1. 在输入text上运行句子标记器并提取标记:
sent_tokenize_list = sent_tokenize(text) 
  1. 打印句子列表以查看是否正确工作:
print("Sentence tokenizer:")
print(sent_tokenize_list) 
  1. 单词分词在 NLP 中非常常用。NLTK 附带了几种不同的单词分词器。让我们从基本的单词分词器开始:
# Create a new word tokenizer 
from nltk.tokenize import word_tokenize 

print("Word tokenizer:")
print(word_tokenize(text)) 
  1. 如果你想要将这个标点符号分割成单独的标记,那么你需要使用WordPunct标记器:
# Create a new WordPunct tokenizer 
from nltk.tokenize import WordPunctTokenizer 

word_punct_tokenizer = WordPunctTokenizer() 
print("Word punct tokenizer:")
print(word_punct_tokenizer.tokenize(text)) 
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Sentence tokenizer:
['Are you curious about tokenization?', "Let's see how it works!", 'We need to analyze a couple of sentences with punctuations to see it in action.']

Word tokenizer:
['Are', 'you', 'curious', 'about', 'tokenization', '?', 'Let', "'s", 'see', 'how', 'it', 'works', '!', 'We', 'need', 'to', 'analyze', 'a', 'couple', 'of', 'sentences', 'with', 'punctuations', 'to', 'see', 'it', 'in', 'action', '.']

Word punct tokenizer:
['Are', 'you', 'curious', 'about', 'tokenization', '?', 'Let', "'", 's', 'see', 'how', 'it', 'works', '!', 'We', 'need', 'to', 'analyze', 'a', 'couple', 'of', 'sentences', 'with', 'punctuations', 'to', 'see', 'it', 'in', 'action', '.']

它是如何工作的...

在这个菜谱中,我们展示了如何将一个复杂的句子分割成许多标记。为此,使用了nltk.tokenize包的三个方法——sent_tokenizeword_tokenizeWordPunctTokenizer

  • sent_tokenize函数返回文本的句子标记副本,使用 NLTK 推荐的句子标记器。

  • word_tokenize将字符串标记化以分割除句点以外的标点符号。

  • WordPunctTokenizer使用正则表达式\w+|[^\w\s]+将文本标记化为一个由字母和非字母字符组成的序列。

还有更多...

分词是一个过程,根据所分析的语言,可能是一个非常复杂的任务。例如,在英语中,我们可能满足于考虑没有空格的字符序列和各种标点符号。在像日语或中文这样的语言中,其中单词不是由空格分开,而是由不同符号的组合,这可能会完全改变意义,任务也更为复杂。但一般来说,即使在单词由空格分开的语言中,也必须定义精确的标准,因为标点符号通常是模糊的。

参见

词干提取文本数据

当我们处理文本文档时,我们会遇到不同形式的单词。考虑单词play。这个单词可以以各种形式出现,如 play、plays、player、playing 等。这些都是具有相似意义的单词家族。在文本分析过程中,提取这些单词的基本形式是有用的。这将帮助我们提取一些统计数据来分析整个文本。词干提取的目标是将这些不同形式减少到共同的基形式。这使用启发式过程来截断单词的末尾以提取基形式。

准备工作

在这个菜谱中,我们将使用nltk.stem包,它提供了一个用于从单词中移除形态词缀的处理接口。对于不同的语言,有不同的词干提取器可用。对于英语,我们将使用PorterStemmerLancasterStemmerSnowballStemmer

如何操作...

让我们看看如何词干提取文本数据:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的stemmer.py文件中):
from nltk.stem.porter import PorterStemmer 
from nltk.stem.lancaster import LancasterStemmer 
from nltk.stem.snowball import SnowballStemmer 
  1. 让我们定义一些用于操作的words,如下所示:
words = ['table', 'probably', 'wolves', 'playing', 'is',  
        'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] 
  1. 我们将定义一个我们想要使用的stemmers列表:
# Compare different stemmers stemmers = ['PORTER', 'LANCASTER', 'SNOWBALL'] 
  1. 初始化所有三个词干提取器所需的必要对象:
stemmer_porter = PorterStemmer() stemmer_lancaster = LancasterStemmer() stemmer_snowball = SnowballStemmer('english') 
  1. 为了以整洁的表格形式打印输出数据,我们需要以正确的方式对其进行格式化:
formatted_row = '{:>16}' * (len(stemmers) + 1) 
print('\n', formatted_row.format('WORD', *stemmers), '\n')
  1. 让我们遍历words列表,并使用以下三个词干提取器对其进行词干提取:
for word in words: stemmed_words = [stemmer_porter.stem(word), stemmer_lancaster.stem(word), stemmer_snowball.stem(word)] print(formatted_row.format(word, *stemmed_words)) 
  1. 如果你运行此代码,你将在你的终端中看到以下输出。观察 LANCASTER 词干提取器对几个单词的不同行为:

它是如何工作的...

所有三种词干提取算法基本上都旨在实现相同的目标。三种词干提取算法之间的区别基本上在于它们操作的严格程度。如果你观察输出,你会看到 LANCASTER 词干提取器比其他两个词干提取器更严格。PORTER 词干提取器在严格性方面是最小的,而 LANCASTER 是最严格的。从 LANCASTER 词干提取器得到的词干往往显得混乱和模糊。该算法非常快,但它会大量减少单词。因此,一个好的经验法则是使用 SNOWBALL 词干提取器。

还有更多...

词干提取是将单词的屈折形式还原到其词根形式的过程,称为词干。词干不一定对应于单词的形态学词根(词元):通常,相关单词被映射到相同的词干就足够了,即使后者不是单词的有效词根。创建词干提取算法一直是计算机科学中的一个普遍问题。词干提取过程应用于搜索引擎的查询扩展,以及其他自然语言处理问题。

参见

使用词形还原将文本转换为它的基本形式

词形还原的目标也是将单词还原到其基本形式,但这是一种更结构化的方法。在先前的菜谱中,您看到我们使用词干提取器获得的基词实际上并没有什么意义。例如,单词 wolves 被还原为 wolv,这并不是一个真正的单词。词形还原通过使用词汇和词的形态学分析来解决这个问题。它移除了屈折词尾,如-ing 或-ed,并返回单词的基本形式。这种基本形式被称为词元。如果您对单词wolves进行词形还原,您将得到wolf作为输出。输出取决于标记是动词还是名词。

准备工作

在这个菜谱中,我们将使用nltk.stem包将单词的屈折形式还原到其规范形式,称为词元

如何做到这一点...

让我们看看如何使用词形还原将文本转换为它的基本形式:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的lemmatizer.py文件中):
import nltk
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer 
  1. 让我们定义与我们在词干提取过程中使用的相同的一组单词:
words = ['table', 'probably', 'wolves', 'playing', 'is', 'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] 
  1. 我们将比较两个词形还原器:NOUNVERB词形还原器。让我们列出它们:
# Compare different lemmatizers 
lemmatizers = ['NOUN LEMMATIZER', 'VERB LEMMATIZER'] 
  1. 基于词形还原器WordNet创建对象:
lemmatizer_wordnet = WordNetLemmatizer() 
  1. 为了以表格形式打印输出,我们需要以正确的方式格式化它:
formatted_row = '{:>24}' * (len(lemmatizers) + 1) 
print('\n', formatted_row.format('WORD', *lemmatizers), '\n') 
  1. 遍历单词并对它们进行词形还原:
for word in words: 
    lemmatized_words = [lemmatizer_wordnet.lemmatize(word, pos='n'), 
           lemmatizer_wordnet.lemmatize(word, pos='v')] 
    print(formatted_row.format(word, *lemmatized_words)) 
  1. 如果您运行此代码,您将看到以下输出:

观察当词形还原器对单词进行词形还原时,NOUNVERB词形还原器的区别,如图所示的前一个截图

它是如何工作的...

词形还原是将单词的屈折形式还原为其规范形式的过程,称为词元。在自然语言处理过程中,词形还原是自动确定给定单词的单词的算法过程。这个过程可能涉及其他语言处理活动,如形态学和语法分析。在许多语言中,单词以不同的屈折形式出现。规范形式与其词性的组合称为单词的词素。在结构词理学中,词素是构成语言词汇的最小单位。因此,每种语言的词汇可能对应于词典中的词元形式。

还有更多...

在 NLTK 中,对于词形还原,有WordNet可用,但这个资源仅限于英语语言。这是一个包含大量英语词汇的大型词汇数据库。在这个包中,名词、动词、形容词和副词被分组为认知同义词集(synsets),每个集合都表达一个独特概念。synsets 通过语义和词汇概念关系相互连接。通过浏览器可以导航这个显著相关的单词和概念的网络。WordNet根据单词的意义分组单词,不仅连接词形(字母字符串),还连接特定的单词。因此,在网络中彼此靠近的单词在语义上是区分的。此外,WordNet还标记单词之间的语义关系。

参见

使用块划分文本

块划分指的是根据任何随机条件将输入文本分割成片段。这与分词不同,因为没有任何限制,并且块不需要有任何意义。这在文本分析中非常常用。在处理大型文本文档时,最好分块进行。

如何做到这一点...

让我们看看如何使用块划分来划分文本:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的chunking.py文件中):
import numpy as np 
nltk.download('brown')
from nltk.corpus import brown 
  1. 让我们定义一个函数来split文本成块。第一步是根据空格划分文本:
# Split a text into chunks  
def splitter(data, num_words): 
    words = data.split(' ') 
    output = [] 
  1. 初始化一些必需的变量:
    cur_count = 0 
    cur_words = [] 
  1. 让我们遍历words
    for word in words: 
        cur_words.append(word) 
        cur_count += 1 
  1. 一旦达到所需的单词数量,重置变量:
        if cur_count == num_words: 
            output.append(' '.join(cur_words)) 
            cur_words = [] 
            cur_count = 0 
  1. 将块追加到output变量中,并返回它:
    output.append(' '.join(cur_words) ) 

    return output 
  1. 我们现在可以定义主函数。从brown语料库加载数据。我们将使用前 10,000 个单词:
if __name__=='__main__': 
    # Read the data from the Brown corpus 
    data = ' '.join(brown.words()[:10000]) 
  1. 定义每个块中的单词数量:
    # Number of words in each chunk  
    num_words = 1700 
  1. 初始化一些相关变量:
    chunks = [] 
    counter = 0 
  1. 在此文本 data 上调用 splitter 函数并打印输出:
    text_chunks = splitter(data, num_words) 

    print("Number of text chunks =", len(text_chunks)) 
  1. 如果你运行此代码,你将在终端中看到生成的分块数量被打印出来:
Number of text chunks = 6

它是如何工作的...

分块(也称为浅层解析)是对一个命题的分析,该命题由一个主语和一个谓语以简单形式构成。主语通常是名词短语,而谓语是由一个动词和零个或多个补语及副词构成的动词短语。一个分块由一个或多个相邻的标记组成。

对于分块问题有众多方法。例如,在指定的任务中,一个分块被表示为一个由方括号分隔的词组,其中有一个表示分块类型的标签。所使用的数据集是从给定的语料库中提取与期刊文章相关的部分,并从语料库的句法树中提取信息块得到的。

还有更多...

布朗大学当代美国英语标准语料库(或简称 brown 语料库)是由亨利·库切拉(Henry Kucera)和 W. Nelson Francis 在 1960 年代在罗德岛普罗维登斯的布朗大学编制的语料库。它包含 500 个英文文本摘录,这些文本摘录来自 1961 年在美国出版的作品,总共有大约一百万个单词。

参考以下内容

构建词袋模型

当涉及到处理由数百万个单词组成的文本文档时,将它们转换为数值表示是必要的。这样做的原因是为了使它们能够用于机器学习算法。这些算法需要数值数据以便分析并输出有意义的信息。这就是词袋方法出现的地方。这基本上是一个从所有文档中的所有单词中学习词汇的模型。它通过构建文档中所有单词的直方图来对每个文档进行建模。

准备工作

在这个菜谱中,我们将使用 sklearn.feature_extraction.text 包构建一个词袋模型来提取文档词矩阵。

如何实现...

让我们看看如何构建一个词袋模型,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已在提供的 bag_of_words.py 文件中):
import numpy as np 
from nltk.corpus import brown 
from chunking import splitter 
  1. 让我们定义 main 函数。从 brown 语料库中加载输入 data
if __name__=='__main__': 
    # Read the data from the Brown corpus 
    data = ' '.join(brown.words()[:10000]) 
  1. 将文本数据分为五个分块:
    # Number of words in each chunk  
    num_words = 2000 

    chunks = [] 
    counter = 0 

    text_chunks = splitter(data, num_words) 
  1. 创建一个基于以下文本分块的字典:
    for text in text_chunks: 
        chunk = {'index': counter, 'text': text} 
        chunks.append(chunk) 
        counter += 1 
  1. 下一步是提取文档词矩阵。这基本上是一个矩阵,它计算文档中每个单词的出现次数。我们将使用scikit-learn来完成这项任务,因为它在这方面比 NLTK 有更好的支持。导入以下包:
    # Extract document term matrix 
    from sklearn.feature_extraction.text import CountVectorizer 
  1. 定义对象并提取文档词矩阵:
    vectorizer = CountVectorizer(min_df=5, max_df=.95) 
    doc_term_matrix = vectorizer.fit_transform([chunk['text'] for chunk in chunks]) 
  1. vectorizer对象中提取词汇并打印:
    vocab = np.array(vectorizer.get_feature_names()) 
    print("Vocabulary:")
    print(vocab)
  1. 打印文档词矩阵
    print("Document term matrix:") 
    chunk_names = ['Chunk-0', 'Chunk-1', 'Chunk-2', 'Chunk-3', 'Chunk-4'] 
  1. 要以表格形式打印它,你需要按以下方式格式化:
    formatted_row = '{:>12}' * (len(chunk_names) + 1) 
    print('\n', formatted_row.format('Word', *chunk_names), '\n') 
  1. 遍历单词并打印每个单词在不同片段中出现的次数:
    for word, item in zip(vocab, doc_term_matrix.T): 
        # 'item' is a 'csr_matrix' data structure 
        output = [str(x) for x in item.data] 
        print(formatted_row.format(word, *output)) 
  1. 如果你运行此代码,你将在终端中看到两个主要输出。第一个输出是词汇表,如下面的截图所示:
Vocabulary:
['about' 'after' 'against' 'aid' 'all' 'also' 'an' 'and' 'are' 'as' 'at'
 'be' 'been' 'before' 'but' 'by' 'committee' 'congress' 'did' 'each'
 'education' 'first' 'for' 'from' 'general' 'had' 'has' 'have' 'he'
 'health' 'his' 'house' 'in' 'increase' 'is' 'it' 'last' 'made' 'make'
 'may' 'more' 'no' 'not' 'of' 'on' 'one' 'only' 'or' 'other' 'out' 'over'
 'pay' 'program' 'proposed' 'said' 'similar' 'state' 'such' 'take' 'than'
 'that' 'the' 'them' 'there' 'they' 'this' 'time' 'to' 'two' 'under' 'up'
 'was' 'were' 'what' 'which' 'who' 'will' 'with' 'would' 'year' 'years']
  1. 第二件事是文档词矩阵,它相当长。前几行看起来如下:

图片

它是如何工作的...

考虑以下句子:

  • 句子 1: 褐色的狗正在跑步。

  • 句子 2: 黑色的狗在黑色的房间里。

  • 句子 3: 在房间里跑步是禁止的。

如果你考虑这三个句子,你将拥有以下九个独特的单词:

  • the

  • brown

  • dog

  • is

  • running

  • black

  • in

  • room

  • forbidden

现在,让我们将每个句子转换成直方图,使用每个句子中单词的数量。每个特征向量将是九维的,因为我们有九个独特的单词:

  • 句子 1: [1, 1, 1, 1, 1, 0, 0, 0, 0]

  • 句子 2: [2, 0, 1, 1, 0, 2, 1, 1, 0]

  • 句子 3: [0, 0, 0, 1, 1, 0, 1, 1, 1]

一旦我们提取了这些特征向量,我们可以使用机器学习算法来分析它们。

更多内容...

词袋模型是一种在信息检索和自然语言处理中用于通过忽略词序来表示文档的方法。在这个模型中,每个文档被认为包含单词,类似于证券交易所;这允许基于列表管理这些单词,其中每个股票包含列表中的某些单词。

相关内容

构建文本分类器

文本分类的主要目的是将文本文档分类到不同的类别中。这是 NLP 中的一个重要分析技术。我们将使用一种基于tf-idf统计方法的技巧,它代表词频-逆文档频率。这是一个分析工具,帮助我们了解一个词在文档集中的重要性。这作为了一个用于分类文档的特征向量。

准备工作

在这个菜谱中,我们将使用词频-逆文档频率方法来评估一个词在集合或语料库中对于一个文档的重要性,并构建一个文本分类器。

如何操作...

让我们看看如何构建一个文本分类器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的tfidf.py文件中):
from sklearn.datasets import fetch_20newsgroups 
  1. 让我们选择一个类别列表并使用字典映射命名它们。这些类别是我们刚刚导入的新闻组数据集的一部分:
category_map = {'misc.forsale': 'Sales', 'rec.motorcycles': 'Motorcycles', 
 'rec.sport.baseball': 'Baseball', 'sci.crypt': 'Cryptography', 
 'sci.space': 'Space'}
  1. 根据我们刚刚定义的类别加载训练数据:
training_data = fetch_20newsgroups(subset='train',  
        categories=category_map.keys(), shuffle=True, random_state=7) 
  1. 导入特征提取器:
# Feature extraction 
from sklearn.feature_extraction.text import CountVectorizer 
  1. 使用训练数据提取特征:
vectorizer = CountVectorizer() X_train_termcounts = vectorizer.fit_transform(training_data.data) print("Dimensions of training data:", X_train_termcounts.shape)
  1. 我们现在准备好训练分类器了。我们将使用多项式朴素贝叶斯分类器:
# Training a classifier 
from sklearn.naive_bayes import MultinomialNB 
from sklearn.feature_extraction.text import TfidfTransformer 
  1. 定义几个随机的输入句子:
input_data = [ "The curveballs of right handed pitchers tend to curve to the left", "Caesar cipher is an ancient form of encryption", "This two-wheeler is really good on slippery roads" 
] 
  1. 定义tfidf_transformer对象并对其进行训练:
# tf-idf transformer 
tfidf_transformer = TfidfTransformer() 
X_train_tfidf = tfidf_transformer.fit_transform(X_train_termcounts) 
  1. 一旦我们有了特征向量,就使用这些数据训练多项式朴素贝叶斯分类器:
# Multinomial Naive Bayes classifier 
classifier = MultinomialNB().fit(X_train_tfidf, training_data.target) 
  1. 使用词频转换输入数据:
X_input_termcounts = vectorizer.transform(input_data) 
  1. 使用tfidf_transformer模块转换输入数据:
X_input_tfidf = tfidf_transformer.transform(X_input_termcounts) 
  1. 使用训练好的分类器预测这些输入句子的输出类别:
# Predict the output categories 
predicted_categories = classifier.predict(X_input_tfidf) 
  1. 按照以下方式打印输出:
# Print the outputs 
for sentence, category in zip(input_data, predicted_categories): 
    print('\nInput:', sentence, '\nPredicted category:', \
            category_map[training_data.target_names[category]])
  1. 如果你运行此代码,你将在你的终端中看到以下输出:
Dimensions of training data: (2968, 40605)

Input: The curveballs of right handed pitchers tend to curve to the left 
Predicted category: Baseball

Input: Caesar cipher is an ancient form of encryption 
Predicted category: Cryptography

Input: This two-wheeler is really good on slippery roads 
Predicted category: Motorcycles

它是如何工作的...

tf-idf 技术常用于信息检索。目标是理解文档中每个词的重要性。我们想要识别在文档中多次出现的词。同时,像isbe这样的常见词并不能真正反映内容的本质。因此,我们需要提取真正的指示词。每个词的重要性随着计数的增加而增加。同时,随着它出现的频率增加,这个词的频率也会增加。这两者往往相互平衡。我们从每个句子中提取词频。一旦我们将其转换为特征向量,我们就可以训练分类器来对这些句子进行分类。

词频TF)衡量一个词在给定文档中出现的频率。由于多个文档的长度不同,直方图中的数字往往变化很大。因此,我们需要对其进行归一化,以便使其成为公平的竞争环境。为了实现归一化,我们可以将词频除以给定文档中的总词数。逆文档频率IDF)衡量一个给定词的重要性。当我们计算 TF 时,所有词都被视为同等重要。为了平衡常见词的频率,我们需要降低它们的权重并放大罕见词。我们需要计算包含给定词的文档数量与总文档数量的比率,然后除以总文档数量。IDF 是通过取该比率的负算法来计算的。

还有更多...

简单的词,如isthe,在各种文档中出现的频率很高。然而,这并不意味着我们可以根据这些词来表征文档。同时,如果一个词只出现一次,那么它也没有用。因此,我们寻找出现次数较多但不会造成噪声的词。这被公式化为 tf-idf 技术,并用于分类文档。搜索引擎经常使用这个工具按相关性排序搜索结果。

参见

识别姓名的性别

识别姓名的性别是 NLP 中的一个有趣任务。我们将使用启发式方法,即姓名的最后几个字符是其定义特征。例如,如果姓名以la结尾,那么它很可能是女性姓名,如 Angela 或 Layla。另一方面,如果姓名以im结尾,那么它很可能是男性姓名,如 Tim 或 Jim。由于我们不确定要使用确切多少个字符,我们将进行实验。

准备工作

在这个菜谱中,我们将使用名称语料库提取标记的名称,然后我们将根据名称的最后一部分来分类性别。

如何实现...

让我们看看如何识别性别:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的gender_identification.py文件中):
import nltk
nltk.download('names')

import random from nltk.corpus import names from nltk import NaiveBayesClassifier from nltk.classify import accuracy as nltk_accuracy 
  1. 我们需要定义一个函数来从输入词中提取特征:
# Extract features from the input word def gender_features(word, num_letters=2): return {'feature': word[-num_letters:].lower()} 
  1. 让我们定义主函数。我们需要一些标记的训练数据:
if __name__=='__main__': 
    # Extract labeled names 
    labeled_names = ([(name, 'male') for name in names.words('male.txt')] + 
            [(name, 'female') for name in names.words('female.txt')]) 
  1. 初始化随机数生成器并打乱训练数据:
    random.seed(7) 
    random.shuffle(labeled_names) 
  1. 定义一些输入名称进行实验:
    input_names = ['Leonardo', 'Amy', 'Sam'] 
  1. 由于我们不知道需要考虑多少个结尾字符,我们将从 15 扫描参数空间。每次,我们将提取以下特征:
    # Sweeping the parameter space 
    for i in range(1, 5): 
        print('\nNumber of letters:', i) 
        featuresets = [(gender_features(n, i), gender) for (n, gender) in labeled_names] 
  1. 将其分为训练集和测试集:
        train_set, test_set = featuresets[500:], featuresets[:500] 
  1. 我们将使用朴素贝叶斯分类器来完成这项工作:
        classifier = NaiveBayesClassifier.train(train_set) 
  1. 对参数空间中的每个值评估 classifier 模型:
        # Print classifier accuracy 
        print('Accuracy ==>', str(100 * nltk_accuracy(classifier, test_set)) + str('%')) 

# Predict outputs for new inputs 
        for name in input_names: 
            print(name, '==>', classifier.classify(gender_features(name, i))) 
  1. 如果你运行此代码,你将在你的终端中看到以下输出:
Number of letters: 1
Accuracy ==> 76.2%
Leonardo ==> male
Amy ==> female
Sam ==> male

Number of letters: 2
Accuracy ==> 78.6%
Leonardo ==> male
Amy ==> female
Sam ==> male

Number of letters: 3
Accuracy ==> 76.6%
Leonardo ==> male
Amy ==> female
Sam ==> female

Number of letters: 4
Accuracy ==> 70.8%
Leonardo ==> male
Amy ==> female
Sam ==> female

它是如何工作的...

在这个菜谱中,我们使用了语料库的名称来提取标记的名称,然后根据名称的最后一部分对性别进行分类。朴素贝叶斯分类器是一种监督学习分类器,它使用贝叶斯定理来构建模型。这个主题在第二章的构建朴素贝叶斯分类器菜谱中有所涉及,即构建分类器

更多内容...

贝叶斯分类器被称为朴素,因为它天真地假设在感兴趣的特定类别中,某个特定特征的呈现或缺失与其他特征的呈现或缺失无关,这极大地简化了计算。让我们继续构建朴素贝叶斯分类器。

相关内容

分析句子的情感

情感分析是自然语言处理中最受欢迎的应用之一。情感分析是指确定给定文本是正面还是负面的过程。在某些变体中,我们将中性视为第三个选项。这项技术通常用于发现人们对特定主题的看法。这用于分析各种形式中用户的情感,如营销活动、社交媒体、电子商务等。

准备工作

在这个菜谱中,我们将通过使用 movie_reviews 语料库中的数据,使用朴素贝叶斯分类器来分析句子的情感。

如何做到这一点...

让我们看看如何分析句子的情感:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的 sentiment_analysis.py 文件中):
import nltk.classify.util 
from nltk.classify import NaiveBayesClassifier 
from nltk.corpus import movie_reviews 
  1. 定义一个提取特征的功能:
def extract_features(word_list): 
    return dict([(word, True) for word in word_list]) 
  1. 我们需要为此准备训练数据,因此我们将使用 NLTK 中的电影评论:
if __name__=='__main__': 
    # Load positive and negative reviews   
    positive_fileids = movie_reviews.fileids('pos') 
    negative_fileids = movie_reviews.fileids('neg') 
  1. 让我们将它们分为正面和负面评论:
    features_positive = [(extract_features(movie_reviews.words(fileids=[f])),  
            'Positive') for f in positive_fileids] 
    features_negative = [(extract_features(movie_reviews.words(fileids=[f])),  
            'Negative') for f in negative_fileids] 
  1. 将数据分为训练集和测试集:
    # Split the data into train and test (80/20) 
    threshold_factor = 0.8 
    threshold_positive = int(threshold_factor * len(features_positive)) 
    threshold_negative = int(threshold_factor * len(features_negative)) 
  1. 提取特征:
    features_train = features_positive[:threshold_positive] + features_negative[:threshold_negative] 
    features_test = features_positive[threshold_positive:] + features_negative[threshold_negative:]   
    print("Number of training datapoints:", len(features_train))
    print("Number of test datapoints:", len(features_test))
  1. 我们将使用一个 NaiveBayesClassifier。定义对象并对其进行训练:
    # Train a Naive Bayes classifier 
    classifier = NaiveBayesClassifier.train(features_train) 
    print("Accuracy of the classifier:", nltk.classify.util.accuracy(classifier, features_test))
  1. classifier 对象包含了它在分析过程中获得的最具信息量的单词。这些单词基本上在将评论分类为正面或负面评论方面具有很大的影响力。让我们把它们打印出来:
    print("Top 10 most informative words:")
    for item in classifier.most_informative_features()[:10]:
        print(item[0])
  1. 创建几个随机的输入句子:
    # Sample input reviews 
    input_reviews = [ 
        "It is an amazing movie",  
        "This is a dull movie. I would never recommend it to anyone.", 
        "The cinematography is pretty great in this movie",  
        "The direction was terrible and the story was all over the place"  
    ] 
  1. 在这些输入句子上运行分类器并获取预测:
    print("Predictions:") 
    for review in input_reviews: 
        print("Review:", review) 
        probdist = classifier.prob_classify(extract_features(review.split())) 
        pred_sentiment = probdist.max() 
  1. 打印输出:
        print("Predicted sentiment:", pred_sentiment) 
        print("Probability:", round(probdist.prob(pred_sentiment), 2))
  1. 如果你运行此代码,你将在终端中看到三个主要输出。第一个是准确性,如下面的代码片段所示:
Number of training datapoints: 1600
Number of test datapoints: 400
Accuracy of the classifier: 0.735
  1. 下一个项目是最具信息量的单词列表:
Top 10 most informative words:

outstanding
insulting
vulnerable
ludicrous
uninvolving
astounding
avoids
fascination
seagal
anna
  1. 最后一个项目是预测列表,这些预测基于输入句子:
Predictions:

Review: It is an amazing movie
Predicted sentiment: Positive
Probability: 0.61

Review: This is a dull movie. I would never recommend it to anyone.
Predicted sentiment: Negative
Probability: 0.77

Review: The cinematography is pretty great in this movie
Predicted sentiment: Positive
Probability: 0.67

Review: The direction was terrible and the story was all over the place
Predicted sentiment: Negative
Probability: 0.63

工作原理...

在这里,我们使用了 NLTK 的朴素贝叶斯分类器。在特征提取函数中,我们基本上提取了所有唯一的单词。然而,NLTK 分类器需要数据以字典的形式排列。因此,我们以这种方式排列它,以便 NLTK classifier 对象可以接受它。一旦我们将数据分为训练集和测试集,我们就训练了分类器,以将句子分类为正面或负面。

如果你查看最具信息量的单词,你可以看到我们有诸如“杰出”这样的词来表示正面评论,以及诸如“侮辱”这样的词来表示负面评论。这是有趣的信息,因为它告诉我们哪些单词被用来表示强烈的反应。

更多内容...

情感分析这个术语指的是使用 NLP 技术、文本分析和计算语言学在书面或口语文本来源中查找信息。如果从大量数据中提取这种主观信息,并且因此从大量人群的意见中提取,情感分析也可以称为意见挖掘

相关内容

使用主题建模在文本中识别模式

主题建模指的是在文本数据中识别隐藏模式的过程。目标是揭示一组文档中的隐藏主题结构。这将帮助我们更好地组织文档,以便我们可以用于分析。这是 NLP 研究的一个活跃领域。

准备工作

在这个菜谱中,我们将使用名为gensim的库,通过主题建模来识别文本中的模式。

如何操作...

让我们看看如何通过主题建模来识别文本中的模式:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的 topic_modeling.py 文件中):
from nltk.tokenize import RegexpTokenizer   
from nltk.stem.snowball import SnowballStemmer 
from gensim import models, corpora 
from nltk.corpus import stopwords 
  1. 定义一个函数来加载数据。我们将使用你已提供的data_topic_modeling.txt文本文件:
# 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 
  1. 让我们定义一个class来预处理文本。这个预处理器将负责创建所需的对象并从输入文本中提取相关特征:
# Class to preprocess text 
class Preprocessor(object): 
    # Initialize various operators 
    def __init__(self): 
        # Create a regular expression tokenizer 
        self.tokenizer = RegexpTokenizer(r'\w+') 
  1. 我们需要一个停用词列表,这样我们就可以从分析中排除它们。这些是常见的词,如intheis等等:
        # get the list of stop words  
        self.stop_words_english = stopwords.words('english') 
  1. 定义SnowballStemmer模块:
        # Create a Snowball stemmer  
        self.stemmer = SnowballStemmer('english') 
  1. 定义一个处理函数,该函数负责分词、去除停用词和词干提取:
    # Tokenizing, stop word removal, and stemming 
    def process(self, input_text): 
        # Tokenize the string 
        tokens = self.tokenizer.tokenize(input_text.lower()) 
  1. 从文本中去除停用词:
        # Remove the stop words  
        tokens_stopwords = [x for x in tokens if not x in self.stop_words_english] 
  1. 对标记执行词干提取:
        # Perform stemming on the tokens  
        tokens_stemmed = [self.stemmer.stem(x) for x in tokens_stopwords] 
  1. 返回处理后的标记:
        return tokens_stemmed 
  1. 现在我们已经准备好定义主函数。从文本文件中加载数据:
if __name__=='__main__': 
    # File containing linewise input data  
    input_file = 'data_topic_modeling.txt' 

    # Load data 
    data = load_data(input_file) 
  1. 定义一个基于我们定义的类的对象:
    # Create a preprocessor object 
    preprocessor = Preprocessor() 
  1. 我们需要处理文件中的文本并提取处理后的标记:
    # Create a list for processed documents 
    processed_tokens = [preprocessor.process(x) for x in data] 
  1. 创建一个基于分词文档的字典,以便它可以用于主题建模:
    # Create a dictionary based on the tokenized documents 
    dict_tokens = corpora.Dictionary(processed_tokens) 
  1. 我们需要使用处理后的标记创建文档-词矩阵,如下所示:
    # Create a document term matrix 
    corpus = [dict_tokens.doc2bow(text) for text in processed_tokens] 
  1. 假设我们知道文本可以分为两个主题。我们将使用一种称为潜在狄利克雷分配LDA)的主题建模技术。定义所需的参数并初始化LdaModel对象:
    # Generate the LDA model based on the corpus we just created 
    num_topics = 2 
    num_words = 4 

    ldamodel = models.ldamodel.LdaModel(corpus,  
            num_topics=num_topics, id2word=dict_tokens, passes=25) 
  1. 一旦它识别出两个主题,我们可以通过查看最有贡献的单词来看到它是如何区分这两个主题的:
    print("Most contributing words to the topics:")
    for item in ldamodel.print_topics(num_topics=num_topics, num_words=num_words):
        print ("Topic", item[0], "==>", item[1])
  1. 完整的代码在topic_modeling.py文件中。如果你运行此代码,你将在你的终端看到以下输出:
Most contributing words to the topics:
Topic 0 ==> 0.057*"need" + 0.034*"order" + 0.034*"work" + 0.034*"modern"
Topic 1 ==> 0.057*"need" + 0.034*"train" + 0.034*"club" + 0.034*"develop"

它是如何工作的...

主题建模通过识别文档中的重要单词或主题来工作。这些单词往往决定了主题的内容。我们使用正则表达式分词器,因为我们只想得到单词,而不想得到任何标点符号或其他类型的标记。因此,我们使用它来提取标记。去除停用词是另一个重要步骤,因为这有助于我们消除由isthe等词引起的噪声。之后,我们需要对单词进行词干提取以得到它们的基形式。整个流程被包装为文本分析工具中的预处理块。这正是我们在做的事情!

我们使用一种称为 LDA 的技术来建模主题。LDA 基本上将文档表示为不同主题的混合,这些主题倾向于产生单词。这些单词以一定的概率产生。目标是找到这些主题!这是一个生成模型,试图找到负责生成给定文档集的主题集。

还有更多...

如您从输出中看到的,我们有诸如 talent 和 train 这样的词来表征体育主题,而我们有 encrypt 来表征密码学主题。我们正在处理一个非常小的文本文件,这就是为什么一些词可能看起来不太相关。显然,如果你处理更大的数据集,准确性将会提高。

参考以下内容

使用 spaCy 进行词性标注

词性标注PoS tagging)是指为与特定词汇类别相对应的单词进行标记的过程。常见的语言类别包括名词、动词、形容词、冠词、代词、副词、连词等等。

准备工作

在这个菜谱中,我们将使用名为spacy的库来进行词性标注。

如何操作...

让我们看看如何使用spacy进行词性标注:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的PosTagging.py文件中):
import spacy
  1. 加载en_core_web_sm模型:
nlp = spacy.load('en_core_web_sm')
  1. 让我们定义一个输入文本:
 Text = nlp(u'We catched fish, and talked, and we took a swim now and then to keep off sleepiness')

作为来源,我使用了基于马克·吐温的小说《哈克贝利·费恩历险记》的一段文字。

  1. 最后,我们将进行词性标注:
for token in Text:
    print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
          token.shape_, token.is_alpha, token.is_stop) 
  1. 以下结果被返回:
We -PRON- PRON PRP nsubj Xx True False
catched catch VERB VBD ROOT xxxx True False
fish fish NOUN NN dobj xxxx True False
, , PUNCT , punct , False False
and and CCONJ CC cc xxx True True
talked talk VERB VBD conj xxxx True False
, , PUNCT , punct , False False
and and CCONJ CC cc xxx True True
we -PRON- PRON PRP nsubj xx True True
took take VERB VBD conj xxxx True False
a a DET DT det x True True
swim swim NOUN NN dobj xxxx True False
now now ADV RB advmod xxx True True
and and CCONJ CC cc xxx True True
then then ADV RB advmod xxxx True True
to to PART TO aux xx True True
keep keep VERB VB conj xxxx True True
off off PART RP prt xxx True True
sleepiness sleepiness NOUN NN dobj xxxx True False

它是如何工作的...

词性标注涉及为文档/语料库中的每个单词分配一个标签。所使用的标签集的选择取决于语言。输入是一个单词字符串和要使用的标签集,输出是每个单词与最佳标签的关联。一个单词可能有多个兼容的标签(歧义)。词性标注器的任务是通过对单词所在上下文中的最合适的标签进行选择来解决这些歧义。

还有更多...

为了进行词性标注,我们使用了spacy库。这个库可以提取语言特征,例如词性标注、依存标签和命名实体,并自定义分词器以及与基于规则的匹配器协同工作。

要安装en_core_web_sm模型,请使用以下代码:

$ python -m spacy download en

相关内容

使用 gensim 进行 Word2Vec

词嵌入使我们能够记住单词的语义和句法信息,从未知语料库开始,构建一个向量空间,其中单词的向量在相同的语言环境中出现时更接近,也就是说,如果它们被识别为语义相似。Word2Vec 是一组用于生成词嵌入的模板;该包最初由 Tomas Mikolov 用 C 语言编写,后来在 Python 和 Java 中实现。

准备工作

在这个菜谱中,我们将使用 gensim 库构建 Word2Vec 模型。

如何操作...

让我们看看如何使用 gensim 进行词嵌入:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 GensimWord2Vec.py 文件中):
import gensim
from nltk.corpus import abc
  1. 基于 Word2Vec 方法构建模型:
model= gensim.models.Word2Vec(abc.sents())
  1. 让我们从数据中提取词汇并将其放入一个 list
 X= list(model.wv.vocab)
  1. 现在,我们将找到与单词 'science' 的相似性:
data=model.wv.most_similar('science')
  1. 最后,我们将 print data
print(data)

将返回以下结果:

[('law', 0.938495397567749), ('general', 0.9232532382011414), ('policy', 0.9198083877563477), ('agriculture', 0.918685793876648), ('media', 0.9151924252510071), ('discussion', 0.9143469929695129), ('practice', 0.9138249754905701), ('reservoir', 0.9102856516838074), ('board', 0.9069126844406128), ('tight', 0.9067160487174988)]

工作原理...

Word2Vec 是一个简单的双层人工神经网络,旨在处理自然语言;该算法需要输入语料库并返回一组向量,这些向量代表文本中单词的语义分布。对于语料库中包含的每个单词,以唯一的方式构建一个向量,以在创建的多维空间中表示它作为一个点。在这个空间中,如果单词被识别为语义上更相似,它们将更接近。

更多内容...

在这个菜谱中,我们使用了澳大利亚国家语料库(abc),这是一个优秀的语言数据集合,包括基于文本和数字的数据。要使用这个语料库,您必须使用以下代码下载它:

import nltk
nltk.download('abc')

相关内容

浅层学习用于垃圾邮件检测

垃圾邮件意味着发送大量不受欢迎的消息(通常是商业性质的)。它可以通过任何媒介实现,但最常用的媒介是电子邮件和短信。垃圾邮件的主要目的是广告,从最常见的商业优惠到非法材料的销售提议,如盗版软件和处方药,以及从可疑的金融项目到真正的欺诈尝试。

准备工作

在这个菜谱中,我们将使用逻辑回归模型进行垃圾邮件检测。为此,将使用为手机垃圾邮件研究收集的标记短信消息集合。这个数据集包含 5,574 条真实的非编码英语消息,根据它们是否合法(ham)或垃圾邮件(spam)进行标记。

如何操作...

让我们看看如何进行浅层学习垃圾邮件检测:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 LogiTextClassifier.py 文件中):
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.model_selection import train_test_split
  1. 加载您提供的 spam.csv 文件:
df = pd.read_csv('spam.csv', sep=',',header=None, encoding='latin-1')
  1. 让我们提取用于训练和测试的数据:
X_train_raw, X_test_raw, y_train, y_test = train_test_split(df[1],df[0])
  1. 我们需要将 DataFrame 中包含的文本数据 vectorize
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
  1. 我们现在可以构建逻辑回归模型:
classifier = LogisticRegression(solver='lbfgs', multi_class='multinomial')
classifier.fit(X_train, y_train)
  1. 定义两个短信消息作为测试数据:
X_test = vectorizer.transform( ['Customer Loyalty Offer:The NEW Nokia6650 Mobile from ONLY å£10 at TXTAUCTION!', 'Hi Dear how long have we not heard.'] )
  1. 最后,我们将使用模型进行预测:
predictions = classifier.predict(X_test)
print(predictions)

将返回以下结果:

['spam' 'ham']

这些表明第一条短信被识别为垃圾邮件,而第二条短信被识别为正常邮件

它是如何工作的...

逻辑回归分析是一种估计回归函数的方法,该函数最好地将二元属性的概率与一组解释变量联系起来。逻辑攻击是一种非线性回归模型,当因变量是二元的时使用。模型的目标是确定一个观测值可以生成因变量一个或另一个值的概率;它也可以根据其特征将观测值分类为两个类别。

还有更多...

除了因变量的测量尺度外,逻辑回归分析与线性回归分析的区别在于,对于这种分析,假设y服从正态分布,而如果y是二元的,其分布显然是二项分布。同样,在线性回归分析中,从回归得到的y估计值从-∞到+∞变化,而在逻辑回归分析中,y估计值在 0 到 1 之间变化。

参见