TowardsDataScience 博客中文翻译 2020(二百五十)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Numpy select()和 where()方法在 Pandas 上创建条件列

原文:https://towardsdatascience.com/creating-conditional-columns-on-pandas-with-numpy-select-and-where-methods-8ee6e2dbd5d5?source=collection_archive---------3-----------------------

一些最有用的熊猫把戏

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

帕斯卡尔·贝纳登在 Unsplash 上拍摄的照片

Pandas 是一个令人惊叹的库,它包含大量用于操作数据的内置函数。虽然内置函数能够执行有效的数据分析,但是合并其他库中的方法为 Pandas 增加了价值。

在本文中,我们将看看如何用 Numpy select()where()方法在 Pandas 上创建条件列

请查看我的 Github repo 获取源代码

从两个选项中创建条件列

假设我们有一个关于水果店的数据集。

df = pd.DataFrame({
    'fruit': ['Apple', 'Banana', 'Apple', 'Banana'],
    'supplier': ['T & C Bro', 'T & C Bro', 'JM Wholesales', 'JM Wholesales'],
    'weight (kg)': [1000,2000,3000,4000],
    'customer_rating': [4.8,3.2, 4.2, 4.3]
})

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

水果店数据集(作者制作)

我们可以看到,商店从两个供应商处购买了水果“ T & C Bro ”和“ JM 批发”。它们的价目表分别保存如下:

# T & C Bro
**tc_price** = pd.DataFrame({
    'fruit': ['Apple', 'Banana', 'Orange', 'Pineapple'],
    'price (kg)': [1.1, 2, 2.9, 3.1]
})# JM Wholesales
**jm_price** = pd.DataFrame({
    'fruit': ['Apple', 'Banana', 'Orange', 'Pineapple'],
    'price (kg)': [1.2, 1.8, 4, 6]
})

我们希望从供应商的价目表中检索价格,将它们组合起来,并将结果添加到新列中。预期的输出是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者制作的图像

这个计算中棘手的部分是,我们需要有条件地检索 价格(公斤) (基于供应商水果),然后将其组合回水果店数据集。

对于这个例子,一个改变游戏规则的解决方案是结合 Numpy where()函数。一行代码就可以解决检索和合并。

第一步:将fruit列设置为索引

df = df.**set_index('fruit')**tc_price = tc_price.**set_index('fruit')**jm_price = jm_price.**set_index('fruit')**

通过调用set_index('fruit') 列设置为所有数据集的索引。这很重要,所以我们可以稍后使用loc[df.index]来选择用于值映射的列。

步骤 2:将 Numpy where()与 Pandas 数据框架合并

Numpy where(**condition**, **x**, **y**)方法[1]根据condition返回从xy中选择的元素。最重要的是,这个方法可以接受类似数组的输入,并返回类似数组的输出。

df['price (kg)'] = **np.where**(
    **df['supplier'] == 'T & C Bro'**, 
    tc_price**.loc[df.index]['price (kg)']**, 
    jm_price**.loc[df.index]['price (kg)']**
)

通过执行上述语句,您应该得到如下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者制作的图像

有点困惑?以下是一些细节:

  • df['supplier'] == 'T & C Bro'返回一个布尔数组
  • df.index返回['Apple', 'Banana', 'Apple', 'Banana'](由 步骤 1 设定的指标)。并且tc_price.loc[df.index]jm_price.loc[df.index]都基于标签df.index返回相同长度的数据帧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

np.where()如何工作

从两个以上的选项中创建条件列

我们已经学习了如何从两个数据集创建一个条件列。如果超过 2 个数据集呢,例如,水果店数据集中有 3 个不同的供应商。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有 3 个供应商的水果店数据集(作者制作)

对于 2 个以上的数据集/选择,我们可以使用 Numpy select()方法。让我们借助一个例子来看看它是如何工作的。

步骤 1:将价格列表组合在一起,并将fruit列设置为索引

第一步是将所有价格列表合并到一个数据集中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

之后,设置 列为索引。

df_3 = df_3.**set_index('fruit')**df_price = df_price.**set_index('fruit')**

步骤 2:将 Numpy select()与 Pandas 数据框架合并

Numpy select(**condlist**, **choicelist**)方法根据condlist中的条件返回一个从choicelist中的元素提取的数组。

args = df_price.loc[df_3.index]**conds = [
    df_3['supplier'] == 'T & C Bro', 
    df_3['supplier'] == 'JM Wholesales', 
    df_3['supplier'] == 'Star Ltd.',
]****choices = [
    args['T & C Bro'], 
    args['JM Wholesales'], 
    args['Star Ltd.'],
]**df_3['price (kg)'] = np.select(**conds**, **choices**)

基本上,

  • 如果条件df_3['supplier'] == 'T & C Bro'满足,则从args['T & C Bro']获取输出元素。
  • 如果条件df_3['supplier'] == 'JM Wholesale'被满足,它从args['JM Wholesale']获取输出元素。
  • 如果条件df_3['supplier'] == 'Star Ltd.'满足,它从args['Star Ltd.']获取输出元素。

通过执行上述语句,您应该得到如下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

np.select()的输出

好了

感谢阅读。

请在我的 Github 上的笔记本中查看源代码。

如果你对机器学习的实用方面感兴趣,请继续关注。

你可能会对我的其他一些熊猫文章感兴趣:

更多可以从我的 Github 中找到

参考

创建用于熊猫分组的自定义聚合

原文:https://towardsdatascience.com/creating-custom-aggregations-to-use-with-pandas-groupby-e3f5ef8cb43e?source=collection_archive---------6-----------------------

我开始使用带有自定义聚合的 groupby,我想与您分享我所学到的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片来自黛比·莫勒Unsplash

Pandas groupby是一个函数,你可以在数据帧上使用它来分割对象,应用一个函数,并组合结果。当您想要将大量数据分组并为每个组计算不同的操作时,此函数非常有用。如果您将聚合函数与您的groupby一起使用,则每次函数运行时,该聚合将为每个组返回一个值。形成组后,可以对分组的数据运行一个或多个聚合。

我今天使用的数据集是 Kaggle 上的亚马逊 50 大畅销书。这个数据集有一些我们可以使用的很好的数字列和类别。导入数据集后,我们可以快速查看一个使用head(1)抓取第一行并用.T转置数据的例子。这里我们可以看到流派是groupby,的一个很好的分类栏,我们可以汇总用户评级、评论、价格和年份。

df = pd.read_csv("bestsellers_with_categories.csv")
print(df.head(1).T)>>> 0
>>> Name         10-Day Green Smoothie Cleanse
>>> Author                            JJ Smith
>>> User Rating                            4.7
>>> Reviews                              17350
>>> Price                                    8
>>> Year                                  2016
>>> Genre                          Non Fiction

现在我们已经快速浏览了这些列,我们可以使用groupby对 Genre 的数据进行分组。在应用groupby之前,我们可以在这个数据集中看到两个体裁类别,非小说和小说,这意味着我们将有两组数据要处理。如果我们想考虑作者或书名,我们可以和小组一起玩,但我们现在将坚持流派。

df.Genre.unique()>>> array(['Non Fiction', 'Fiction'], dtype=object)group_cols = ['Genre']
ex = df.groupby(group_cols)

为系列数据创建自定义聚合

在设置我们的组之后,我们可以开始创建自定义聚合。我曾在类似这样的函数中使用自定义聚合,在不同条件下执行计算或聚合之前过滤掉特定的值。当我测试聚合函数时,我喜欢从一个小系列开始来验证输出,如下图所示。

ser = pd.Series([1,10,4,2,10,10])

一旦我们有了一系列要测试的数据,我们就可以开始创建聚合函数了。下面我创建了三个聚合函数。第一个和第二个函数是non_nan_meanstandard_deviation,它们验证序列不为空,移除任何 NA 值,并执行平均值或标准偏差计算。最后一个聚合是一个mean_lower_rating,它消除任何大于 5 的上限值,并计算下限值的平均值。

def non_nan_mean(x):
    if x.empty:
        return None
    else: 
        x = x.dropna()
        return np.mean(x)def standard_deviation(x):
    if x.empty:
        return None 
    else: 
        x = x.dropna()
        return np.nanstd(x)

def mean_lower_rating(x):
    upper = []
    for val in x:
        if val > 5:
            continue 
        else: 
            upper.append(val)
    return np.mean(upper)

一旦你定义了你的聚合函数,根据你的需要,你可以应用你的序列来测试。我打印出我的值来查看,如下所示。

print(non_nan_mean(ser))
print(standard_deviation(ser))
print(mean_lower_rating(ser))>>> 6.166666666666667
>>> 3.9334745737353156
>>> 2.3333333333333335

通过 Groupby 应用聚合

在理解了您正在使用的数据集并使用少量数据测试了聚合函数之后,您可以应用使用前面提到的agg函数创建的聚合函数。这个函数作用于 dataframes,它允许我们聚合指定轴上的数据。应用这些聚合的一个简单方法是创建一个列表,并将该列表作为参数传递。此方法会将您的聚合应用到组数据框架中的所有数字列,如下面的示例一所示。在这个例子中,你可以看到我正在调用ex,这是前面的分组输出。

示例 1:

aggs = [non_nan_mean, standard_deviation,mean_lower_rating]
ex2 = ex.agg(aggs)
print(ex2)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter notebook 中生成的字典聚合的输出。

从输出中可以看出,mean_lower_rating聚合在特定的列上表现不佳,这是由为特定列设计的函数(即用户评级)造成的。考虑到这一点,我们可以看看将聚合参数传递到agg函数的不同方式,这将清理输出。

将参数传递给agg的另一种方法是开发一个字典。字典将列名映射到要运行的聚合函数。这种方法的一个例子见例二。

示例 2:

aggs_by_col = {'Reviews': [non_nan_mean], 
               'Price': [non_nan_mean,standard_deviation],
               'User Rating': [mean_lower_rating]}
ex1 = ex.agg(aggs_by_col)
print(ex1)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter notebook 中生成的字典聚合的输出。

如前所述,如果您不想使用mean_lower_rating聚合将所有聚合应用于所有列,则首选此方法。定义聚合应用于哪些列的过程对于大型数据集非常有益,因为它清理了输出,只提供了您想要查看的数据。这种应用聚合的方法允许我只为用户评级指定mean_lower_rating聚合,并将其他聚合指定到它们各自的列。

最后的想法

将 Pandas groupbyagg函数一起使用将允许您将数据分组到不同的类别中,并将您的数字列聚合到每个聚合函数的一个值中。如果您有创建自定义聚合函数的用例,您可以编写这些函数来接收一系列数据,然后使用列表或字典将它们传递给agg。如果希望所有的聚合都应用于所有的数字列,可以传递一个列表;如果要指定哪些聚合应用于哪些列,可以传递一个字典。如果您愿意,还可以使用 lambda 函数来创建聚合,这一点我在本文中没有涉及。

附加阅读

如果你想阅读更多,看看我下面的其他文章吧!

[## 每位数据科学家的 8 大技能

当我参加大学讲座时,最常被问到的问题是“我需要具备什么技能?”

towardsdatascience.com](/top-8-skills-for-every-data-scientist-79e6b1faf3e1) [## 停止浪费你的时间,咨询一个主题专家

在从事数据科学项目时,请一位主题专家来审查您的工作可能会有所帮助。

towardsdatascience.com](/stop-wasting-your-time-and-consult-a-subject-matter-expert-f6ee9bffd0fe) [## 采用现有数据科学项目的成功关键

代码本来可能不是你的,但现在是你的了。那么接下来呢?

towardsdatascience.com](/keys-to-success-when-adopting-a-pre-existing-data-science-project-9f1225fb0275) [## 不要太骄傲而不愿寻求帮助

如果你被一个 bug 卡住了或者感到不知所措,你可以寻求你需要的帮助。

towardsdatascience.com](/dont-be-too-proud-to-ask-for-help-76f21d16f318) [## 数据可视化的前 3 篇文章

如果您想更好地构建数据可视化,这些文章很有帮助。

towardsdatascience.com](/top-3-articles-for-data-visualization-956a08a54b04)

为深度学习项目创建自定义图像数据集

原文:https://towardsdatascience.com/creating-custom-image-datasets-for-deep-learning-projects-6e5db76158d4?source=collection_archive---------23-----------------------

下载图像数据集的一些有用的浏览器扩展

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这个周末,我为我学龄前的孩子制作了一个简单的水果分类器。这是一个简单的图像分类应用程序,可以预测图像中的水果。我把它作为一个游戏呈现给我的儿子,看谁先预测到这个名字——计算机还是人类:)。这是一个应用程序的预览。

水果分类器应用程序(作者视频)

对于这个应用程序,我需要下载许多水果的图像来训练图像分类器。在这个过程中,我发现了一些浏览器扩展,这使得批量下载图像变得非常容易,我已经编译并在本文中展示了它们。

然而,在开始使用扩展之前,有两件重要的事情需要记住:

版权问题

不要下载任何违反版权条款的图片。有时,没有所有者的许可,你不能复制版权图像。本文中下载的图片仅用于教育目的。

下载设置

确保您的下载设置中没有选择“在下载前询问每个文件的保存位置”,否则,下载器会询问您是否允许下载每个文件。不可取。下面的片段演示了访问该选项的过程。

下载设置(按作者分类的视频)

这篇文章是寻找好数据集的完整系列文章的一部分。以下是该系列中包含的所有文章:

第 1 部分 : 为数据分析任务获取数据集——高级谷歌搜索

第 2 部分 : 为数据分析任务寻找数据集的有用站点

第三部分 : 为深度学习项目创建定制图像数据集

第四部分 : 毫不费力地将 HTML 表格导入谷歌表单

第 5 部分 : 使用 Camelot,从 pdf 中提取表格数据变得很容易。

第六部分 : 从 XML 文件中提取信息到熊猫数据框架

第 7 部分 : 磨练您探索性数据分析技能的 5 个真实世界数据集

现在让我们来看看一些方便下载图像的有用工具:

1.Fatkun 批量下载图像

Fatkun 批量下载图像是一个强大和方便的浏览器扩展,从网上下载图像。它的一些功能包括:

  • 可以根据分辨率或链接过滤图像
  • 创建自定义规则来下载所需的图像,以及
  • 能够批量重命名和批量下载图像

🔗链接下载扩展

使用

现在让我们下载苹果水果的图像,因为我们想要创建一个水果分类检测器。因为展示比写过程更容易,所以我包含了一个简短的视频来一步一步地展示下载过程。

Fatkun 批量下载图像演示(视频由作者提供)

2.Imageye —图像下载器

Imageye 是另一个浏览器扩展,允许你下载网页上的所有图片。Imageye 还为您提供了以下功能:

  • 基于像素宽度和高度过滤图像。您也可以根据图像的 URL 过滤图像。
  • 像 Fatkun 一样,你可以一次批量下载所有图片,或者手动选择你想要下载的图片。

🔗下载扩展的链接

使用

Imageye —图像下载程序演示(视频由作者提供)

3.下载所有图像

这个 Chrome 扩展从一个网页上下载所有的图片,并把它们打包成一个 zip 文件。它不能根据图片的大小来过滤图片,但对于从 Unsplash 等网站批量下载图片来说却是极好的,因为 Unsplash 只提供图片。它分析当前浏览器页面以识别图像,然后将它们下载到一个 zip 文件中。单击右上角的扩展图标开始下载图像。它会给你一个估计需要多长时间才能完成。

🔗 链接下载扩展

使用

下载所有图像扩展演示(视频由作者提供)

4.ImageAssistant 批量图像下载程序

ImageAssistant 批量图像下载 r 是一个图像提取器,用于从网页中嗅探、分析和批量下载图像。它非常灵活,并提供了许多定制图像下载的方法。例如,您可以提取网页上的图片或预取图像链接,甚至批量提取图像的 URL。此外,图像过滤器还提供了通过图像扩展类型或分辨率大小过滤图像类型显示的选项。

🔗 链接下载扩展

使用

ImageAssistant 批量图像下载程序演示(视频由作者提供)

5.快速方法

最后一种方法不使用任何浏览器扩展。这个方法是我从 Zacchary Mueller 的Practical-Deep-Learning-for-Coders-2.0资源中获得的,他在 Github 上分享了这个资源。这个代码已经由弗朗西斯科·英厄姆和杰瑞米·霍华德的工作给出,而后者又受到 阿德里安·罗斯布鲁克 的启发。

该方法要求你安装fastai**—**一个深度学习库,因为它利用了它的一些固有功能。要理解幕后发生的事情,您需要一些库的知识,尤其是数据块 API。解释这超出了本文的范围,但是我将快速浏览下载图像所需的步骤:

  • 进入谷歌图片搜索你感兴趣的图片。向下滚动,直到找到您想要下载的图像。假设我们对寻找苹果和芒果的图像感兴趣。
  • 在 Chrome/Firefox 中打开 Javascript“控制台”,粘贴以下代码行,然后执行。这将获得所有图像的 URL,并将其保存在一个 CSV 文件中。对每个类别重复该过程。现在你会有两个 CSV 文件,即 apple.csv 和 mango.csv。
urls=Array.from(document.querySelectorAll('.rg_i')).map(el=> el.hasAttribute('data-src')?el.getAttribute('data-src'):el.getAttribute('data-iurl')); 

window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));
  • 接下来,为您要下载的每一类图像创建一个文件夹。
folders = ['Apple','Mango']
files = ['apple.csv','mango.csv')
  • 最后,下载图片
classes = ['Apple','Mango']
path = Path('fruits')path.mkdir(parents=True, exist_ok=True)for i, n in enumerate(classes):
   print(n)
   path_f = Path(files[i])
   download_images(path/n, path_f, max_pics=50)
  • 验证图像是否正确
imgs = L()
for n in classes:
   print(n)
   path_n = path/n
   imgs += verify_images(path_n.ls())

显示图像

fruits = DataBlock(blocks=(ImageBlock, CategoryBlock),
       get_items=get_image_files,
       splitter=RandomSplitter(0.2),
       get_y=parent_label,
       item_tfms=RandomResizedCrop(460),
       batch_tfms=[*aug_transforms(size=224,max_warp=0),Normalize.from_stats(*imagenet_stats)])   
dls = fruits.dataloaders(path,  bs=32)
dls.show_batch(max_n=9)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下载的图像(作者提供的图像)

下面是一段展示整个过程的视频:

演示(作者提供的视频)

结论

在本文中,我们看到了收集图像数据以创建深度学习模型的各种方法。您可以选择浏览器扩展,也可以通过编码来获得相同的结果。无论您选择哪种方法,请注意限制和版权问题。此外,不要忘记使用这些工具为您的下一个项目收集数据。同时,这里是本系列中的一些其他文章,您可能会觉得有用

用 matplotlib 创建自定义绘图函数

原文:https://towardsdatascience.com/creating-custom-plotting-functions-with-matplotlib-1f4b8eba6aa1?source=collection_archive---------5-----------------------

一个简短的教程,学习如何创建模块化函数,包括使用 matplotlib 绘图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TLDR:使用以下语法定义您自己的函数,这些函数涉及绘制到特定的轴上:

def custom_plot(x, y, ax=None, **plt_kwargs):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, **plt_kwargs) ## example plot here
    return(ax)def multiple_custom_plots(x, y, ax=None, plt_kwargs={}, sct_kwargs={}):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, **plt_kwargs) #example plot1
    ax.scatter(x, y, **sct_kwargs) #example plot2
    return(ax)

你可以在这个链接找到原始代码库。

介绍

在之前的一篇文章中,我向你展示了如何更好地组织你的数据。我们看到了如何使用支线剧情整齐地显示不同的情节,如何添加自由浮动轴,以及如何使用 GridSpec 轻松地创建轴的平铺组织。

因为这篇文章的重点是总体图的一般结构和表示,所以绘图本身非常简单,因为它们只使用了一个预定义的 matplotlib 函数,比如带有默认参数的.plot.hist。通常,在您在前一篇文章中学习的漂亮的平铺结构中,您将需要绘制自己的特定绘图,该绘图结合了来自不同类型的基本绘图函数的信息以及对其他一些数据生成或数据处理函数的调用。例如,在顶部绘制随机样本的分布及其相应的理论密度函数。

在这里,我将向您展示如何创建您自己的自定义绘图函数,可以通过在您组织的绘图中调用这些函数来轻松使用这些函数,如下所示:

fig, axes = plt.subplots(number_of_subplots)
for ax in axes:
    my_custom_plotting_function(ax=ax, function_kwargs)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

加上支线剧情的良好组织,这将有助于您最大限度地利用 matplotlib 上的静态绘图(预示着动力学绘图后续教程…也许……)并利用来自不同地块的信息来分享您的数据的综合情况。

基本语法

传递轴

在图形中拥有一系列自定义图的第一步是能够将单个自定义图连接到单个轴。第一步是能够将我们想要绘制的轴传递给我们的自定义函数。这可以像这样简单地完成:

def custom_plot(x, y, ax=None, **plt_kwargs):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, **plt_kwargs) ## example plot here
    return(ax)

我在那里做了什么?这里第一个相关的部分是ax的论证。如果你以前用过 seaborn,你可能已经知道如何使用它了。本质上,ax将获取您想要绘制的轴对象。这可以是一个子图轴或一个简单的自由浮动的嵌入轴。这个想法是,情节的组织部分将在这个功能之外处理,可能由另一个功能处理。

为什么ax会默认为None?这可以用下面的句子来更好地回答:

if ax is None:
        ax = plt.gca()

我们看到,如果在ax中没有提供 axes 对象,它默认为None并触发这个if条件。在这种情况下,由于没有给定轴,默认情况下,该函数将查找当前图形中使用的最后一个轴,或者如果没有可用的轴,则使用函数.gca(代表获取当前轴)创建一个轴,并将其用作绘图轴。在函数的最后,我们还返回这个 ax,以防我们想用它进行其他定制(在某些情况下不是必需的,但很实用)。

让我们测试一下,首先在不指定轴的情况下绘图,然后提供一个特定的轴:

# Without providing axes (default to None -> gca())
plt.figure(figsize=(10, 5))
custom_plot([1, 2], [10, 20])
plt.title('Our custom plot with no axes provided (defaults to .gca())')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# Providing the axes
fig, axes = plt.subplots(2, figsize=(10, 5))# Plotting with our function
custom_plot([2, 3], [4, 15], ax=axes[0])
axes[0].set(xlabel='x', ylabel='y', title='This is our custom plot on the specified axes')# Example plot to fill the second subplot (nothing to do with our function)
axes[1].hist(np.random.normal(size=100))
axes[1].set_title('This plot has nothing to do with our function. Just a histogram of some random numbers')plt.tight_layout() #This to avoid overlap of labels and titles across plots
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

传递图的关键字参数

到目前为止还不错;我们可以创建一个函数来绘制数据,并可以将它连接到我们的绘图的特定轴(如果没有提供轴,它甚至会自己处理)。那么**plt_kwargs呢?

如果您不习惯在函数中使用**kwargs(如在关键字参数中)(参数的实际名称并不重要,您可以将其命名为**kwargs**plt_kwargs**literally_anything_else,只要您加上双星号“**”),那么首先创建并使用一个没有**kwargs的新函数会更容易解释。

顺便说一句,如果你以前真的没有见过这种类型的星号符号,那么在 python 中使用单星号*和双星号**在很多情况下都非常有用,无论是在函数内部还是外部,绝对值得在 google 上搜索一下(甚至可以写一篇关于它的博文…可能…).无论如何,回到我们没有**kwargscustom_plot的例子:

def no_kwargs_plot(x, y, ax=None):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y) ## example plot here
    return(ax)plt.figure(figsize=(10, 5))
no_kwargs_plot([1, 2], [10, 20])
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

没有错误,没有问题…但是,如果你想让线条变粗呢?通常在.plot()中,我们会简单地将参数linewidth设置为一个更厚的值。我们可以将linewidth添加到no_kwargs_plot的输入列表中,然后像这样将其传递给.plot():

def no_kwargs_plot(x, y, ax=None, linewidth=1):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, linewidth) ## example plot here

那会解决问题。但是所有其他可能的论点呢?剧情()。必须将它们连同它们的默认值一起写在我们的函数中,这将会很长,并且不太实际:

def no_kwargs_plot(x, y, ax=None, linewidth=1, other=1,...):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, linewidth, other,....) ## example plot here

这就是使用**符号(**kwargs)有用的地方。当用于自由键值元素时,比如我们函数中的孤立输入(在我们的例子中,这些输入与预定义的参数 x、y 和 ax 无关)**name会将所有这些元素打包到一个字典中,并将它们存储在变量name中。

例如,如果我们将绘图函数用作custom_plot(x=xdata, y=ydata, ax=axes[0], linewidth=2, c='g'),那么得到的plt_kwargs字典将是{'linewidth':2, 'c':'g'}。如果这还不太清楚,看看下面的示例代码,输出(> >)和下面的模式:

def print_kwargs_only(x, y, ax=None, **plt_kwargs):
    print(plt_kwargs) #to print the dictionary with all the orphan kwargsprint_kwargs_only(x=[1, 2], y=[10, 20], not_xyax=1,   random_orphan_kwarg='so lonely', linewidth=2, c='g')>> {'not_xyax': 1, 'random_orphan_kwarg': 'so lonely', 'linewidth': 2, 'c': 'g'}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,使用**解决了将所有可能的绘图输入放入我们的函数中的问题,而不需要显式地预定义它们,并让它们准备好在字典中使用。但是,这本补充关键字参数词典是如何使用的呢?

之前,我提到过**在自由元素上使用时表现得像一个打包函数。当你在一个字典上使用**时(不管它是否被**打包过),实际上**会做与之前相反的动作:它会将字典解包成不同的自由元素。在custom_function中,当我们在.plot() ax.plot(x, y, **plt_kwargs)中编写**plt_kwargs时,我们实际上是在要求 python 获取字典plt_kwargs并将它的所有键值对分别解包到.plot()函数中作为单独的输入。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样,在不知道将使用多少和哪些绘图定制的情况下,我们可以将它们全部传递给将进行绘图的函数部分。

我们可以再次使用我们最初的custom_plot函数看到这一点(您可能注意到,这次我使用了该函数返回的轴来展示如何使用它):

plt.figure(figsize=(10, 5))
out_ax = custom_plot([1, 2], [10, 20], linewidth=5, c='g')
out_ax.set(xlabel='xlabel', ylabel='ylabel', title='Testing out the usefulness of **kwargs')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基本语法的扩展

这就是基本的语法。有了这些,你应该已经能够开始创作一些更有趣的情节了。

不过在使用 ham 之前,我们需要注意一个你在使用**kwargs时可能会遇到的潜在问题。也就是说,如果您在custom_plot函数中进行多个绘图会怎么样?例如,如果你正在绘制两条线,其中一条应该是虚线,而另一条是实线。**kwargs怎么知道哪个论点进入了哪个情节?

答案是“**kwargs包装机”不能再工作了,需要更换,但是“**kwargs拆包机”可以正常工作。我这么说是什么意思?让我们定义一个名为multiple_custom_plots的新函数来阐明它:

def multiple_custom_plots(x, y, ax=None, plt_kwargs={}, sct_kwargs={}):
    if ax is None:
        ax = plt.gca()
    ax.plot(x, y, **plt_kwargs)
    ax.scatter(x, y, **sct_kwargs)
    return(ax)

这里有什么不同,我们应该如何使用它?首先,看看可能的输入列表。现在,我们没有了**kwargs,而是有了两个新的参数,一个用于我们的一个情节。此外,默认情况下,这些参数是空字典。

如果你听了我之前关于**kwargs的解释,希望你已经很清楚了。想法是,因为我们不能要求函数自动将所有孤立输入打包到一个字典中(我们现在需要两个独立的字典),我们将不得不自己提供预打包的每个绘图参数字典。

稍后用双星号使用它们与最初的custom_plot没有什么不同,因为在字典上使用**仍然意味着我们希望它的值被解包。我们使用空字典作为缺省值,因为如果您没有提供定制字典,我们在试图用**解包它们(或缺少它们)时会遇到问题。如果没有提供任何内容,空字典本质上是用来将任何内容解包到函数中的。

让我们看看如何使用它:

plot_params = {'linewidth': 2, 'c': 'g', 'linestyle':'--'}
scatter_params = {'c':'red', 'marker':'+', 's':100}
xdata = [1, 2]
ydata = [10, 20]plt.figure(figsize=(10, 5))
multiple_custom_plots(xdata, ydata, plt_kwargs=plot_params, sct_kwargs=scatter_params)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

快速应用

因此,当要创建自定义函数来进行绘图时,上一节应该足以让您在一段时间内享受到静态绘图的乐趣。在下一节中,我将简单地给你一个使用自定义函数的绘图示例,希望能启发你去做一些自己的绘图。

随机样本的样本容量和核密度估计

假设您想了解给定随机变量的样本大小如何影响对其潜在概率分布的估计。

假设我们有一个连续的随机变量 X,正态分布,均值为μ(μ),标准差为σ(σ)(即 X∞N(μσ ))。我们想知道 scipy 的核密度估计量(kde)如何受到我们随机样本大小的影响(我们从正态分布中随机抽样的次数),方法是将其与潜在的真实概率密度分布(pdf)的估计量进行比较。

我们将通过绘制不同 n 值的样本本身、它们的 kde 和它们的潜在 pdf 来做到这一点。

def sample_plot(mu=0, sigma=1, N=100, sct_kwargs={}, pdf_kwargs={}, kde_kwargs={}, ax=None):
    # generate sample
    sample = np.random.normal(loc=mu, scale=sigma, size=N)

    # generate pdf
    xrange = mu + 5*sigma*np.linspace(-1, 1, 100)
    pdf = stats.norm.pdf(xrange, loc=mu, scale=sigma)

    # generate kde
    estimation = stats.gaussian_kde(sample)
    kde = estimation(xrange)

    #Plotting
    if ax is None:
        ax = plt.gca()   
    ax.scatter(sample, np.zeros_like(sample), **sct_kwargs)
    ax.plot(xrange, pdf, **pdf_kwargs)
    ax.plot(xrange, kde, **kde_kwargs)
    return(xrange)

让我们一步一步地解构这个函数:

首先是投入。这里,我们将从高斯随机数生成器创建自己的数据,而不是请求数据数组。所以我们需要询问相关的统计参数μ和σ(分别为高斯分布的均值和标准差)。我们还需要问要取的样本数 N。实际上,我们将在后面迭代 N 的不同值,以查看样本大小对估计的影响。这个想法是将样本绘制成散点,将 pdf 和 kde 绘制成常规线图。因此,我们将为它们各自的绘图参数(线宽、标记大小等)提供一个字典作为输入。).最后,我们将询问我们想要在其上绘制这三样东西的图形的轴。

该函数的第一部分将简单地从所提供的统计参数中生成大小为 N 的随机高斯样本。

代码的第二部分将创建对应于由μ和σ给出的正态分布的 pdf 的线图的 x-y 对。我们将 pdf 的范围限制为 5 个标准偏差,因为任何一边的任何其他值都将非常小。

代码的第三部分首先计算样本的 kde,然后将其应用到与 pdf 相同的 x 轴上的值范围。

最后,在代码的第四部分,我们简单地将 x 轴(高度为 0)上的所有采样值绘制成散点图,并将 pdf 和 kde 绘制成线图。这三个都有各自的绘图关键字参数。

# Sample parameters
sample_sizes = (10, 20, 100, 250, 500, 2_000)
mean = 100
std = 15# Plotting parameters
scatter_params = {'alpha':0.1, 'c':'g', 's':100, 'label':'samples'}
pdf_params = {"linewidth":2, 'c':'k', 'label':'pdf'}
kde_params = {"linewidth":3, 'ls':'--', 'c':'g', 'label':'kde'}# Plotting
fig, axes = plt.subplots(6, figsize=(15, 20))
for ax, n in zip(axes, sample_sizes):
    sample_plot(mu=mean, sigma=std, N=n, ax=ax,
                sct_kwargs=scatter_params, pdf_kwargs=pdf_params, kde_kwargs=kde_params)
    ax.set_title(f'N={n}')axes[0].legend()
axes[-1].set_xlabel('Sample Value', fontsize=13)
plt.tight_layout()
plt.savefig('finalplot')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就是这样!希望您已经学会了如何通过正确传递相应的轴和关键字参数来为函数添加绘图功能。这将有助于您拥有越来越模块化的代码来快速浏览和可视化您的数据。

原载于 2020 年 4 月 28 日https://matic derini . github . io

为只读数据库创建自定义 SQL Server 复制

原文:https://towardsdatascience.com/creating-custom-sql-server-replication-for-read-only-databases-e25953d7ff82?source=collection_archive---------29-----------------------

我们来讨论一下使用 T-SQL 的 SQL Server 复制。并考虑从源到目的地单向复制表的一般算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Piqsels (CC0)

通常需要创建 SQL Server 数据库的只读副本。例如,为了将分析任务和操作任务分开,可能需要这样做。第一种情况会导致数据库的高负载,为了降低负载,会创建主数据库的副本来执行分析性只读查询。

通常,这些只读副本可以使用内置的 DBMS 工具创建:

  1. 原木运输
  2. SQL Server 复制
  3. AlwaysOn 可用性组

但是,如果您不需要整个数据库,而只需要其中的几个表,该怎么办呢?在这种情况下,您可以自己创建复制。只要数据采样是主要目标,单向的数据库复制(主到从)就足够了。几种方法包括 SSIS 和。NET 可以用来执行这种复制。

在本文中,我们将使用 JobEmpl recruiting service 数据库来演示如何使用 T-SQL 创建主-从方向的数据库复制。

使用 T-SQL 创建 SQL Server 单向复制

首先,让我们描述一下这个复制的主要原理和算法。在每次迭代中,我们需要比较源数据库和目标数据库中所选表的数据。这意味着我们需要输入一个唯一的代理键来比较这些表。为了加快比较过程,我们还需要为那个键创建一个索引。还需要为每个复制表添加一个计算字段,为每一行计算校验和。

选择固定的数据部分也很重要,例如,一次选择一定数量的行(每次迭代)。

因此,我们需要执行以下步骤:

  1. 在源表上,创建一个 REPL_GUID 列和一个唯一的 REPL_GUID 索引,以强制源表和目标表之间的一对一关系。您还应该创建一个计算校验和列,该列将计算每一行的校验和值。
  2. 创建一个名为 Target 的新目标数据库。
  3. 跨源和目标数据库同步复制表的模式,并删除对不存在的对象的所有引用。
  4. 禁用目标数据库的外键。
  5. 运行复制并监视源数据库和目标数据库之间有多少行不同。

现在让我们使用为招聘员工而创建的 JobEmpl 数据库来详细回顾每个步骤。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1 求职者数据库模式

我们只需要复制雇员工作历史表。

然后,可以在以下脚本的帮助下执行上述算法的第一步。

从脚本中,您可以看到它必须在源 JobEmpl 数据库上运行,并且您应该相应地在 @src@sch 变量中指定源数据库和模式。构建动态 sql 需要 @sql 变量,同时需要 @name 保存复制表的名称。

首先,我们将复制的表名收集到临时#tbl 表中。接下来,我们用光标遍历每个表名,并将表名提取到@name 变量中。之后,对于每个表,形成一个非 IDENTITY 类型的列列表,并将结果插入到带有“+”符号的@listcols 变量中。

值得一提的是,首先使用 CAST 函数将每个表名转换为 NVACHAR(MAX)类型,然后使用 COALESCE 函数([,N ’ ')。这样做是为了从每一行的所有列值中形成一个字符串。

接下来,创建计算校验和字段、REPL_GUID 字段及其唯一的 indREPL_GUID 索引。

在我们的例子中,我们得到了下面的脚本。

稍后,您可以借助以下脚本从数据库中删除创建的表和索引。

复制的表也在这里,其中每个表的 indREPL_GUID 索引以及 REPL_GUID 和 CheckSumVal 列都被删除了。

在我们的例子中,创建了下面的 T-SQL 代码。

现在让我们根据上面提到的算法的第二步创建一个新的 JobEmplRead 数据库来接收数据。然后,我们同步复制表的模式。要执行同步,请使用 DbForge 模式比较工具:选择 JobEmpl 作为数据源,jobEmplRead 作为数据目标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2 模式同步的数据库选择

然后按下比较按钮。完成比较的元数据创建过程后,选择所需的表并开始配置数据库同步过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3 选择表进行模式同步

接下来,我们选择默认值—脚本生成。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4 选择脚本生成作为同步输出

现在,让我们清除备份创建选项。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5 取消选择备份创建选项

接下来,我们取消选中所有依赖项,因为我们不需要创建其他对象。我们稍后将在生成的模式同步脚本中手动删除外键。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6 取消选择所有依赖关系

现在按下同步按钮,忽略摘要选项卡上的警告。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7 警告

在生成的脚本中删除以下外键:

  • FK _ 工作历史 _ 公司 _ 公司 ID
  • FK _ 工作历史 _ 职位 _ 职位标识
  • FK _ 作业历史记录 _ 项目 _ 项目标识

我们需要这样做,因为我们没有转移公司职位项目表。结果,我们得到了一个移动复制模式表的脚本。

在 JobEmplRead 数据库中运行此脚本。

因此,我们已经完成了算法的第 3 步:在 JobEmpl 和 JobEmplRead 数据库中同步表模式,并删除了对不存在的对象的所有引用。

让我们使用下面的脚本进行监控。

这里我们有完整的外部连接语句创建和表列表的返回,以及不同的行数,包括不存在的和缺少的行。

在我们的例子中,我们得到以下结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8 复制表中不同的行数

下面的脚本是为了比较而生成的。

值得注意的是,为了减少阻塞,事务隔离级别是脏读。

让我们将算法的第 4 步和第 5 步合并到下面的脚本中。

首先,JobEmplRead 数据库中复制表的所有外键都被禁用。然后,使用 MERGE 语句,数据被部分复制。在我们的例子中,每次迭代有 100 000 行。该脚本包含一次迭代,并执行以下 T-SQL 代码。

这个脚本应该按照预先指定的时间间隔自动运行。例如,它可以每分钟运行一次,甚至更频繁,具体取决于分析需求。

在几次迭代之后,不同行的数量必须更少。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9 特殊行数量的变化

请记住,要在禁用的表中启用外键,您应该运行以下脚本。

在我们的例子中,将生成并执行以下脚本。

ALTER TABLE [JobHistory] CHECK CONSTRAINT [FK_JobHistory_Employee_EmployeeID];

请记住,在复制所有数据之前,不能在复制运行时在复制的表上启用外键。

结论

我们已经讨论了实现从源到目的地单向复制表的过程的方法之一。

这种方法和脚本可以应用于任何数据库。但是当然,这些脚本必须根据复制表的具体情况进行修改。例如,如果表中有计算字段,可能需要进行修改。

SQL Complete 是帮助我构建这些脚本的主要工具。该工具还允许代码格式化以及重命名对象及其所有引用。

【https://blog.devart.com】原载于 2020 年 3 月 19 日

从零开始创建深度神经网络,强化学习导论

原文:https://towardsdatascience.com/creating-deep-neural-networks-from-scratch-an-introduction-to-reinforcement-learning-6bba874019db?source=collection_archive---------20-----------------------

第二部分:强化学习和反向传播

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一只正在学习捡东西的狗【图片由 Unsplash 上的 Humphrey Muleba 拍摄】

本文是三部分系列的第二部分,将详细介绍 OpenAI gym 上 Cartpole-v1 问题的解决方案——仅使用 python 库中的 numpy。

的第一部分奠定了基础,创建了计划的大纲并构建了前馈功能,以将环境状态传播到其行动值。这一部分将集中讨论强化学习中累积奖励和行动价值背后的理论,以及反向传播机制的建立。这些是我们代理学习过程的基础。

到上一篇文章结束时,我们有了一个简单的程序循环:在每个时间步上,代理观察环境的状态,并通过它的神经网络(随机初始化)来获得每个动作的预测值。然后,它(以概率 1-ε)选择具有最大预测回报的行动。然后在下一个时间步重复这个过程。

然而,此时代理不知道在采取特定动作之后发生了什么,因为没有反馈。例如,当我们训练一只宠物时,根据它们的行为是可取的还是不可取的,我们创建正面或负面强化的反馈回路是很重要的。一只狗记得在过去捡球为它赢得了一次款待(一个有价值的奖励),并且在下一次类似的情况出现时更有可能优先捡球。同样,我们在程序中实现内存也很重要,这样代理可以跟踪奖励和采取行动后的结果状态。

体验回放

我们将通过在代理的记忆中添加体验,并在一个称为体验重放的过程中使用它们来改进代理来做到这一点。照此修改主程序循环,

# The main program loop
for i_episode in range(NUM_EPISODES):
    observation = env.reset()
    # Iterating through time steps within an episode
    for t in range(MAX_TIMESTEPS):
        env.render()
        action = model.select_action(observation)
 **prev_obs = observation**        observation, reward, done, info = env.step(action)
        # Keep a store of the agent's experiences
 **model.remember(done, action, observation, prev_obs)**
 **model.experience_replay(20)**        # epsilon decay
        ...

我们还将向 RLAgent 添加相关代码,首先是在 init 函数中,

self.memory = deque([],1000000)

还有*记住,*的宣言

def remember(self, done, action, observation, prev_obs):
        self.memory.append([done, action, observation, prev_obs])

请注意,这个内存实现使用了 dequee,这是一个简单的数据结构,允许我们“附加”内存并跟踪最新的 n 条目,其中 n 是 dequee 的大小。Deque 是一个集合,所以我们还需要添加它的 import 语句,

from collections import deque

最后,我们添加了 experience_replay 方法,这将帮助代理从经验中学习以改进其游戏性,

1 def **experience_replay**(self, update_size=20):
2    if (len(self.**memory**) < update_size):
3        return
4    else: 
5    batch_indices = np.random.choice(len(self.**memory**), update_size)
6    for index in batch_indices:
7       done, action_selected, new_obs, prev_obs = self.**memory**[index]
8       action_values = self.**forward**(prev_obs, remember_for_backprop=True)
9       next_action_values = self.**forward**(new_obs, remember_for_backprop=False)
10      experimental_values = np.copy(action_values)
11      if done:
12         *experimental_values[action_selected] = -1*
13      else:
14         *experimental_values[action_selected] = 1 + self.****gamma*****np.max(next_action_values)*
15      self.**backward**(action_values, experimental_values)
16      self.**epsilon** = self.**epsilon** if self.**epsilon** < 0.01 else self.**epsilon***0.995
17   for layer in self.**layers**:
18      layer.lr = layer.lr if layer.lr < 0.0001 else layer.lr*0.995

在这个方法中有很多东西需要解开,但是让我们一步一步地来看。

首先,有一个简单的检查,看看我们是否有足够的经验开始学习。如果没有,我们就等着看。

接下来,我们从所有存储的内存中随机取样,得到 update_size 内存的索引。对于这些索引中的每一个,我们检索(第 7 行)与之相关的内存数据。

然后我们计算三样东西——

  1. prev_obs 变量计算的 action_values
  2. obs 变量计算的 next_action_values ,用于在下一次计算中计算实验值
  3. 实验值(在累积奖励和值函数部分说明)

一旦计算出这些值,我们就将实验值和行动值预测反馈给一个自我。 后向 函数将计算这些值之间的差异,并使用它来对权重矩阵进行更改。 向后 功能将在后面关于反向传播的章节中进行检查和实现。

最后,我们更新系统的ε(探索率)和学习率变量。学习速率是反向传播算法使用的一个属性,它决定了学习过程中步长的大小。注意,我们已经将 epsilon 更新从它在主循环中的原始位置移到了这个方法中。

动作和实验值

上面粘贴的代码块在第 8–14 行有 3 个计算。我们现在将逐一介绍这些内容。

第一个, *action_values,*是代理在给定状态下每个动作的当前估计值(prev _ OBS)——与第一部分的 select_action 函数中计算和使用的值相同。

第二个计算是 *next_action_values。*这是来自下一个状态(new_obs)的两个动作的一组预测值,即在代理在创建该记忆的情节期间采取动作之后获得的状态。

next_action_values 只是一个临时变量,用于随后的计算——用于*实验值。*这是我们的代理从这次特殊“经历”中学到的目标值,有两种形式:

  1. [第 11-12 行]如果在下一次观察中杆子已经翻倒,则该集结束,并且在此状态之后所有未来奖励的总和为(-1)。
  2. [第 13-14 行]如果极点在下一次观察中没有翻倒,那么预期的未来奖励是观察到的即时奖励加上一个折扣的总和,该折扣是该状态的预期未来奖励的 gamma — 倍。

请注意,这两种形式的实验值仅适用于该集期间选择的动作。其他动作(在横竿问题中只有一个)没有更新,因为我们没有任何关于来自给定状态的那些动作的新的实验知识。

实验值数量至关重要。它捕捉新的经验信息,即代理已经了解在给定状态下采取行动的价值。

累积报酬和价值函数

本节将后退一步,形式化一些强化学习理论,这些理论隐含在我们到目前为止编写的代码中。首先,请注意,到目前为止,我们已经讨论了特定状态下的行为价值,但是这到底意味着什么呢?这个值是如何获得的?为什么变量 experimental_values 是这样计算的?

本节的其余部分将讨论一些强化学习理论。他们自由引用并大量借鉴了 2015 年发表的一篇伟大论文,这篇论文展示了深度 Q-Networks 的力量,并使用一个通用的架构来玩 Atari 2600 游戏。如果您对此不感兴趣,请随意跳到反向传播部分,我们将继续实施。

我们首先将特定时间步长 t 的“累积奖励”或“回报”定义为一个时间步长后直到剧集结束的所有未来奖励的总和。形式上,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中,rₜ是每个时间点收到的直接奖励,γ是折现因子。我们还定义了一个称为最优行动值函数 Q(s,a)* —

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是所有政策中国家-行动对(sₜ,aₜ)的“预期”累积回报的最大值,其中政策定义了在给定的国家必须采取什么行动。它代表给定状态下行动的“真实”价值,即从特定状态 *s、*选择行动 *a、*开始,然后在完全了解环境的情况下进行最优博弈的最大预期回报。期望符号抓住了环境通常可能是随机的这一事实。

上面的最佳动作值函数服从一个称为贝尔曼方程的重要恒等式,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个恒等式的意思是,如果我们在状态 s 中采取行动 a 后,从结果状态s’中知道所有行动的最优行动值,那么最优行动值函数 Q(s,a) 是在这个行动后观察到的即时回报的预期和(贴现的)可以从状态s’采取的所有行动a’的最大最优行动值。期望符号捕捉了这样的事实,即s’*(因此也是 r ) 可能在概率上由初始状态 s 和动作 a 确定。

大多数强化学习代理通过使用贝尔曼方程作为迭代更新来学习,在我们的例子中是—

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

i 趋向于无穷大时,收敛到最优作用值函数 Q*(s,a)的一个量。尽管它与上面代码块中的第 14 行相似,但我们所做的和这个等式所代表的是有区别的。这种迭代更新建议我们保存一个所有可能的状态动作对的表,并递增地更新该表中的每个值。这实际上是不可能的,因为我们有连续的状态空间!相反,我们使用基于神经网络的函数逼近器 Q(s,a;W) 来估计动作值函数*,*其中 W 表示我们网络中的权重。这个函数逼近器被称为 Q 网络——在我们的例子中是深度 Q 网络或 DQN。我们确实一直在建设一个 DQN!

代替更新所有状态-动作对,更实际的是迭代地最小化损失函数的期望,定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里有两点需要注意,

  1. 给定代理记忆中的所有(s,a,r,s’)组合,我们使用随机梯度下降来计算每步中单个样本(我们在第 7 行检索的记忆数据)的损失,而不是计算并最小化该值的总预期损失。
  2. 上式中权重 W 的下标(i - 1)和(I)表示我们保存了权重的快照,用于下一次更新。这确实是理想和完整的实现,我们很可能会在第三部分谈到它,但是现在,我们不会实现这个细节。我们只使用一组权重来计算预测值和目标值(即上面代码块中的 experimental_values )。实际上,在没有用于计算目标值的固定网络的情况下,对上述损失函数运行随机梯度下降最终会最小化经验损失和目标值方差之和,这不是我们想要的(我们只想最小化经验损失)。你可以在这里阅读更多关于这个细节【第 9 页】。对于我们的简单用例,幸运的是,这种差异不会破坏我们的解决方案。

有了这些知识,我们现在可以回过头来回答行动价值的意义这个问题。

在训练中的给定时间点,特定状态的动作值(由正向函数计算)就是模型对当时最佳动作值函数的估计。换句话说,它是对代理人期望从一个给定的州获得的每个行为的总折扣奖励的估计。在 cartpole 问题的特定情况下,这是代理期望从特定状态保持存活的时间步数的折扣和。另一方面,实验值是网络在每次迭代中试图接近的目标值。

根据上面提到的几点,损失函数的最后一个等式根据经验可以归结为—

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用实验值动作值,正如我们在前面章节经验 _ 回放功能的定义中所定义的。

这是我们将在每次迭代中寻求最小化的数量(对于给定的样本)。实验值动作值之间的差值将形成更新我们网络权重的基础,在反向功能中实现。

反向传播

强化学习理论帮助我们在最后一节将损失函数定义为实验值和行动值之间的平方差。现在我们有了这些值,优化问题与通过反向传播最小化任何其他神经网络中的误差函数是一样的。

我们现在将实现rl agent . experience _ replay代码的最后一部分,即向后函数:

def **backward**(self, calculated_values, experimental_values): 
  delta = (calculated_values — experimental_values)
  for layer in reversed(self.**layers**):
  delta = layer.**backward**(delta)

该函数首先计算计算值(预测值 action_values )与在某个状态下采取某个动作的实验值之差。然后,该误差通过从最后一个隐藏层到输入层的每一层向后“传播”,并且基于该误差更新权重。直观地说,每一层都被告知其输出的估计值与在输出层中生成实验值所需的预期输出相差多少。

此函数调用 NNLayer.backward 函数:

1 def **backward**(self, gradient_from_above):
2    adjusted_mul = gradient_from_above
3    # this is pointwise
4    if self.**activation_function** != None:
5        adjusted_mul = np.multiply( relu_derivative(self.**backward_store_out**),gradient_from_above)
6        D_i = np.dot( np.transpose(np.reshape(self.**backward_store_in**, (1,len(self.**backward_store_in**)))), np.reshape(adjusted_mul, (1,len(adjusted_mul))))
7        delta_i = np.dot(adjusted_mul, np.transpose(self.**weights**))[:-1]
        self.**update_weights**(D_i)
        return delta_i

首先,(第 4-5 行)如果该层的输出有一个相关的激活函数,我们将激活函数的导数(在我们的例子中是 ReLU 函数)与从上一层接收的误差逐点相乘。得到的值— adjusted_mul — 用于每层中的两个进一步的计算:

  1. D_i 【第 6 行】:这是损失函数相对于该层权重的实际导数。然后将该值传递给 NNLayer。 update_weights 方法进行实际更新。没有处理矩阵的所有绒毛,这是在这一层接收的(列矩阵)输入和上面计算的(row_matrix) adjusted_mul 值之间的简单点积。
  2. delta_i 【第 7 行】:这是该层输入的计算“误差”(即前一层的输出)。这被向后传递到前一层,以实现其自己的导数和增量计算。

在这篇指导性的博客中给出了反向传播算法的推导和一步一步实现的完整处理,但我们不会在此详述。虽然我的实现与那篇博客中给出的略有不同,但检查我描述的函数是否正确地实现了反向传播可能是一个很好的练习。

该算法剩下的两部分是 relu_derivativeNNLayer更新 _ 权重功能。让我们现在过一遍。

ReLU 函数的导数非常简单—如果值大于 0,ReLU 函数返回输入值,否则返回 0。所以这个函数的导数对于 x>0 就是恒等式(即 1)函数,否则就是 0。这是矩阵输入的实现—

def **relu_derivative**(mat):
  return (mat>0)*1

最后, NNLayer.update_weights 函数:

def **update_weights**(self, gradient):
        self.**weights** = self.**weights** - self.**lr***gradient

太好了,这个简单的实现应该足以让一个收敛算法工作并训练我们的代理!这两部分所有代码的工作实现可以在我的 Github 上找到。

训练有素的特工

当我们运行这个程序时(记得在每个时间步长调用 env.render() !)*,*几百集下来,我们就有了一个相当训练有素的经纪人。一旦代理人在连续 100 集内达到 195 以上的平均奖励,任务就被认为解决了。这通常需要 100-300 个时间步来实现,但是,由于不恰当的初始化,在某些运行中可能需要更长的时间。关于收敛保证和性能的更详细的分析将在下一篇文章中完成,但这里有一个来自该代理的完整运行示例,

训练有素的钢管舞特工!

太好了!看起来我们的代理人能够很好地平衡杆子。将此与我们在上一篇文章中开始的随机代理进行比较。

虽然我们构建的代码足以训练这个代理,但它还可以进行优化,以实现更快的收敛和更高的稳定性。这将是下一个帖子的重点。

咻!我们在这方面做了很多工作。这里有一个总结:

  1. 实现了一个“体验重放”机制,其中代理将所有(状态、动作、奖励、下一个状态)存储在内存中,并从中随机抽取样本进行权重更新。
  2. 研究了强化学习理论及其与 cartpole 问题的相关性,并推导出一种更新权重的机制,以最终学习最佳状态-动作值。
  3. 实现了反向传播算法来实际训练网络。

这里还有一些任务需要完成,我们将在本系列的下一篇也是最后一篇文章中继续讨论。

  1. update_weights 方法替换为基于 Adam 的优化器,该优化器跟踪所有参数的个体学习率。这将有助于算法更快地收敛。
  2. 重构全局常量和特定于模型的常量,以获得更好的可配置性。
  3. 分析该方法在几种不同超参数配置下的性能。
  4. 实施用于计算目标值的快照网络,该网络会定期更新为网络的当前 Q 值。

下次再见!

链接 第一部分&第三部分

参考

  1. 用深度强化学习玩雅达利,Deep mind Technologies【Mnih et。阿尔,2015】。
  2. 通过深度强化学习进行人类水平的控制【Mnih et。阿尔,2015】。
  3. http://www . briandolhansky . com/blog/2013/9/27/人工神经网络-反向传播-第四部分
  4. 深度 Q 学习的理论分析。阿尔,2019]

从零开始创建深度神经网络,强化学习导论

原文:https://towardsdatascience.com/creating-deep-neural-networks-from-scratch-an-introduction-to-reinforcement-learning-95bcb493a0c9?source=collection_archive---------37-----------------------

第三部分:反思和改进

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上的 bantersnaps 拍摄的照片

这是一系列文章中的第三篇也是最后一篇,旨在全面介绍 OpenAI gym 上的侧翻问题的解决方案,该解决方案是在没有使用 Pytorch 或 Tensorflow 等标准机器学习框架的情况下从零开始构建的。完整的代码可以在这里找到。
第一部奠定了基础。在其中,我们讨论了神经网络体系结构,并实现了前向传播来计算代理动作的值。

第二部分深入研究了强化学习理论的细节,形式化了 Q 值和 DQN 的概念。我们还在第二部分中实现了反向传播。

第三部分将包含一些不同配置的代理性能的可视化和反映。这最后一部分还将完成实现并添加增强功能,如 Adam 优化器。在这里,我们不太关注超参数选择背后的严格性,而是更多地探索可以为模型改进而调整的配置。

在上一节的最后,我们完成了 Cartpole 代理的实现。时间来看看结果和代理的表现随着时间的推移!

跟踪代理的改进

让我们进行一次完整的训练,从随机初始化开始跟随代理,直到它学会平衡极点的艺术。这次训练用了 141 集来实现它的目标(连续 100 集的平均分数为 195)。

首先,这是分数的图表—

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在让我们看看代理在培训过程中的 3 个不同阶段的表现。代理的前 5 次运行非常糟糕,

最初几轮

在培训的中途,我们可以看到代理取得了进步,尽管仍有改进的空间。这是第 75 和 76 集,

训练中途

最后,在训练接近尾声时,代理人能够几乎完美地平衡杆子。这是第 138 轮,

最终受训代理人

我们可以看到代理到最后还是挺不错的!

目标网络

第二部分关于累积奖励和行动值的部分,我们讨论了如何使用 DQN 的完整实施的简化版本,通过使用相同的权重来计算预测的行动值和目标值。相反,我们需要在计算目标动作值(实验 _ 值在 RLAgent 中)时有一个固定的权重网络。经验 _ 回放方法)。让我们开始实施吧。首先,我们在 NNLayer 中添加了 stored_weights 参数的初始化。初始化函数,

def __init__(self, input_size, output_size, activation=None, lr = 0.001):
        self.input_size = input_size
        self.output_size = output_size
        self.weights = np.random.uniform(low=-0.5, high=0.5, size=(input_size, output_size))
        **self.stored_weights = np.copy(self.weights)**
        self.activation_function = activation
        self.lr = lr

记住参数remember _ for _ back prop= False 中传入的 experimental_values 的计算(通过 next_action_values 计算)。这个参数实际上可以被重新使用来告诉网络使用存储的权重而不是当前的网络权重。编辑 NNLayer。前进功能:

# Compute the forward pass for this layer
    def forward(self, inputs, remember_for_backprop=True):
        # inputs has shape batch_size x layer_input_size 
        input_with_bias = np.append(inputs,1)
        unactivated = None
        **if remember_for_backprop:
            unactivated = np.dot(input_with_bias, self.weights)
        else: 
            unactivated = np.dot(input_with_bias, self.stored_weights)**
        # store variables for backward pass
        output = unactivated
        ...

最后,在每次体验重放后,我们将把存储的权重更新为新的网络权重。将粗体行添加到 experience_replay 方法的最后一位:

...
       for layer in self.layers:
 **layer.update_stored_weights()**           layer.lr = layer.lr if layer.lr < 0.0001 else layer.lr*0.99

最后,添加 NNLayer。更新 _ 存储 _ 权重方法:

def update_stored_weights(self):
   self.**stored_weights** = np.copy(self.**weights**)

很好,这个相对简单的修正意味着我们的目标网络计算不依赖于我们当前的权重。

解决方案的平均发作次数

很好,现在我们已经完成了一次典型的跑步,是时候看看代理在多次不同的训练中学习得有多快了。为了做到这一点,我们从头开始初始化一个新的代理,运行多次,看看需要多少集才能达到平均奖励阈值。

这是 50 次运行的数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每次运行的解决方案集

除了两次运行长时间陷入局部极小值并花费了超过 2000 个时间步骤来解决之外,几乎所有其他运行都花费了不到 200 集来收敛。在 50 次运行中要解决的平均事件数是 240.84。这包括两次异常运行。

不同的批量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

超过 20 次运行的平均发作到解决方案

该图显示了改变批次大小如何影响平均发作次数(每个批次大小超过 20 次)。我测试了 4 个不同的值——5、10、20 和 40。就要解决的平均情节数而言,表现最好的是批量大小为 20,平均有大约 173 个情节要解决。然而,考虑到我们对批量大小为 10 的算法进行了一半的更新,我们仍然能够平均只解决 304 集。这比 double 低 15%左右。在批量大小为 40 的情况下,尽管大多数时候该算法收敛得非常快(超过 50%的解决方案处于最低可能的 100 集标记),但是该算法在某些集内非常不稳定,并且直到远远超过 3000 集时才收敛。

接下来,我们将使用批量大小10 进行其余的增强。

Adam 优化器

到目前为止,在计算了梯度之后,我们的 NNLayer。 update_weights 函数使用随时间不断降低的学习率更新层权重,直到达到最小阈值。

对于权重矩阵中的每个参数,我们当前的权重更新具有相同的学习率。我们现在将使用亚当优化技术,看看是否能改善结果。Adam 的工作方式是跟踪网络中每个参数的个人学习率,使用关于该参数的梯度的一阶和二阶矩的估计值。这通常会导致更快的收敛。

参考这篇文章来了解下面代码的细节。如果您想直接了解超参数配置,可以跳过本节关于 Adam 实现的其余部分。

我们开始吧。我们将更改 NNLayer 中的 update_weights 方法,如下所示:

def **update_weights**(self, gradient):        
        m_temp = np.copy(self.m)
        v_temp = np.copy(self.v) 

        m_temp = self.**beta_1***m_temp + (1-self.**beta_1**)*gradient
        v_temp = self.**beta_2***v_temp + (1-self.**beta_2**)*(gradient*gradient)
        m_vec_hat = m_temp/(1-np.power(self.**beta_1**, self.**time**+0.1))
        v_vec_hat = v_temp/(1-np.power(self.**beta_2**, self.**time**+0.1))
        self.**weights** = self.**weights** - np.divide(self.**lr***m_vec_hat, np.sqrt(v_vec_hat)+self.**adam_epsilon**)

        self.m = np.copy(m_temp)
        self.v = np.copy(v_temp)

beta_1、beta_2 和 adam_epsilon 参数是在 adam 优化器的实现中使用的常数。它们几乎从未改变。矩阵 m 和 v 以及时间参数是在训练过程中更新的变量。它们都在层的 init 方法中初始化:

def **__init__**(self, input_size, output_size, activation=None, lr = 0.001):
        ...
        self.**lr** = lr
        self.**m** = np.zeros((input_size, output_size))
        self.**v** = np.zeros((input_size, output_size))
        self.**beta_1** = 0.9
        self.**beta_2** = 0.999
        self.**time** = 1
        self.**adam_epsilon** = 0.00000001

我们还用 Adam 的时间参数的增加来代替层学习速率的减少。Adam 使用时间自动降低学习率。像这样更新 experience_replay 方法的最后 3 行:

...
   for layer in self.layers:
      **layer.update_time()
 **     layer.update_stored_weights()

update_time()实现只是每次将时间参数增加 1。

将我们的实现与代码开头链接的文章进行比较,以验证它确实是准确的!

太好了,现在是实现的时候了,看看它是否真的表现得更好!以下是 50 次运行(批量为 10 次)后解决方案的集数图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用 Adam optimizer 解决情节

虽然这仍然有一些不稳定性,但与我们的旧优化器相比,它的性能提高了大约 17%(261 对 304)。

虽然这不是结论性的,试验的数量也很少,但它表明 Adam 在某些情况下是一种有效的技术。对 Adam 性能的全面分析,以及何时使用这种优化技术与其他优化技术的对比,可以在原始论文中找到。

隐藏层尺寸

隐藏层的大小也有所不同。以下是 4 种不同隐藏层大小(12、24、48 和 96)的平均集数。在第一部分中的神经网络图描述的每个实验中有两个隐藏层,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为隐藏层解决不同大小的剧集

该图呈下降趋势,图层大小为 96 时表现最佳。同样,少量的运行并不能提供确凿的证据,但它表明更多的参数通常会提高代理的性能。当然,代价是训练更大的网络所需的时间和内存通常更大。

隐藏层数

到目前为止,我们所有的实验都有两个隐藏层。相反,尝试使用 1 层和 3 层,在 20 次运行中得到以下结果,每次运行有 96 个隐藏单元—

  1. 1 层收敛的平均步骤— 198.6
  2. 收敛到两层的平均步数— 163.75
  3. 3 层的平均收敛步骤— (>1000)集。在这里,网络需要很长时间才能收敛。此外,更深层次的神经网络会遇到其他问题,比如需要小心处理的消失梯度问题

不幸的是,硬件的限制使我无法对更深层次的神经网络进行更彻底的分析。

摘要

在这一部分中,我们总结并完成了算法的实现,以训练我们的 cartpole 代理。包括目标网络实现和 Adam 的更新代码可在这里找到。由于普遍性低,我故意没有对这个特殊问题的各种超参数配置进行全面分析,但这里有一篇的信息论文,它非常详细地研究了改变 dqn 配置的影响。

综上所述,这部分我们做了以下工作。

  1. 从随机初始化到接近完美平衡的最终状态,分析并可视化了代理改进的样本运行。
  2. 添加了目标网络,完成了实施。
  3. 增加了一个 Adam 优化器来代替原来的一揽子学习率。
  4. 探索了一些不同的超参数配置,如批量大小、隐藏层大小和隐藏层数。

进一步阅读

如果您已经完成了这一步,那么您现在已经完成了横竿问题的实现!您可能希望:

  1. 进一步调整这个程序,找出最佳的超参数配置。你能在 50 次运行中把平均集数降到 110 以下吗?
  2. 继续讨论在露天健身房环境中的其他问题。 MountainCar 是很好的下一步!
  3. 看看强化学习和人工通用智能的前沿。OpenAI 在他们的博客上记录了所有的进展。

感谢您的阅读!!!

参考

  1. 重要的深度强化学习【彼得·亨德森等】
  2. Adam——深度强化学习的最新趋势
  3. Adam 优化技术简介

从零开始创建深度神经网络,强化学习导论

原文:https://towardsdatascience.com/creating-deep-neural-networks-from-scratch-an-introduction-to-reinforcement-learning-part-i-549ef7b149d2?source=collection_archive---------12-----------------------

第一部分:体育馆环境与 DNN 建筑

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

宠物强化学习![图片鸣谢: 斯蒂芬妮·吉布奥特

本文是三部分系列文章的第一部分,将详细介绍 OpenAI gym 上 Cartpole-v1 问题的解决方案——仅使用 python 库中的 numpy。这个解决方案远非最佳解决方案(你可以在健身房网站上找到),而是专注于从基本原则出发。
运行本文代码的先决条件是 python (3.x ),并安装了 gym 和 numpy 模块。

当我第一次开始在 OpenAI 健身房寻找强化学习时,我无法找到任何关于如何开始自己构建解决方案的好资源。有非常强大的库(如 Tensorflow 和 Pytorch ),允许您构建极其复杂的神经网络,并轻松解决 cartpole 问题,但我想从头开始创建神经网络,因为我相信理解现代机器学习技术的核心构建块是有价值的。我写的是我希望在我努力工作的时候能够找到的东西。所以让我们开始吧。

一、什么是 OpenAI 健身房?在他们的网站上有一个很好的简短介绍,“Gym 是一个开发和比较强化学习算法的工具包。”“ gym 库是一个测试问题——环境——的集合,你可以用它来制定你的强化学习算法。这些环境有一个共享的接口,允许你编写通用算法。”这意味着,围绕构建和渲染模拟真实世界场景的模型的工程已经为我们完成,所以我们可以专注于教代理玩好游戏。

上面的描述也提到了强化学习。那是什么?这里有一个维基百科的总结——“强化学习机器学习的一个领域,涉及软件代理应该如何在一个环境中采取行动以最大化一些累积回报的概念。”

给你一个类比,想想一只狗是如何被训练的——有利的行为被积极地强化(以款待的形式),而负面的行为被消极地强化。在某种程度上,即使是我们人类,也是复杂的强化学习代理,试图通过选择我们认为在未来会“有益于”我们(以更大回报的形式)的行动来最大化实现我们目标的机会。这张图展示了强化学习的循环,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

强化学习周期[Image credit:Mohit Mayank

上图显示了一个代理(我们将构建的程序)将环境的状态和奖励作为输入,从之前的动作中选择一个后续动作并将其反馈给环境,然后再次观察环境。

很好,现在我们已经了解了强化学习中的基本概念,让我们回到我们试图解决的问题——cart pole。首先,具体看一下关于侧翻问题的文档。文档很好地概述了我们正在努力实现的目标。简而言之,我们控制着滑块的底部,顶部有一根垂直平衡的杆子。我们的目标是尽可能长时间地防止杆子脱落。如果极点(就其角度而言)下降到某个点以下,环境将被重置。下面是一个随机代理人在处理横竿问题。

扁担上的随机代理

如你所见,不是很好!但这是意料之中的,因为这个代理会忽略环境的当前状态,并在每个时间步选择一个随机动作。让我们看看我们是否能做得更好。

履行

是写代码的时候了!我们将从导入将要使用的库开始。我们将需要 gym 用于上面讨论的开放人工智能环境,以及 numpy 用于一些数学和矩阵操作。

import gym
import numpy as np

接下来,我们需要导入 gym 为侧翻问题提供的环境。这是如何做到的:

env=gym.make('CartPole-v1')

我们还可以通过打印来观察这个特殊环境空间的一些特征:

print(env.action_space) # Discrete(2)
print(env.observation_space) # Box(4,)

文档中有更多关于环境及其工作方式的信息,但是上面的值捕捉了定义这个环境的基本元素——可以执行的动作,以及在每个时间步的观察。

动作空间是离散的,包含 2 个值:0 和 1。这些对应于代理能够执行的两个动作,即向左或向右推动滑块。

另一方面,观察空间是连续的,并且有四个组成部分(不要与数据结构框(4)相混淆,对于我们的目的,它仅仅意味着包含四个值的数组)。四个值是什么意思?它们是代表当时环境状态的数字——即大车的位置、大车的速度、杆子的角度、杆子的转速。[https://github . com/open ai/gym/issues/238 # issue comment-231129955]

这里要理解的一个基本事情是,观察和动作空间中的数字的含义只是为了完整性而解释的,我们的目标不是解释(动作空间或观察空间的)值,而是让代理学习这些值在上下文中的含义。让我们回到我们的程序,添加代码来运行一个基本的循环。

# Global variablesNUM_EPISODES = 10
MAX_TIMESTEPS = 1000# The main program loopfor i_episode in range(NUM_EPISODES):
    observation = env.reset()
    # Iterating through time steps within an episode    for t in range(MAX_TIMESTEPS):
        env.render()
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        if done:
            # If the pole has tipped over, end this episode            break

上面的代码为剧集声明了一个主程序循环,并遍历了一集内的时间步长。在内部循环中,程序采取行动,观察结果,然后检查情节是否已经结束(要么是杆子倒下了,要么是滑块脱离了边缘)。如果有,环境被重置,内部循环重新开始。

选择动作的行是从可用的动作中随机抽样的;事实上,它的行为与前面显示的随机代理完全一样。让我们修改一下,定义一个自定义方法来选择给定观察的动作。

现在,我们将定义一个代理(希望如此!)将学会在给定的状态下更聪明地选择行动。我们将在一个类中模拟这个代理:

**class RLAgent:
**    env = None def **__init__**(self, env):
        self.***env*** = env

    def **select_action**(self, observation):
        return env.action_space.sample()

我们还需要向全局变量添加一个 RLAgent 实例,并更改操作选择,以便从实例化的类中调用该函数。注意,目前 select_action 函数做的事情和以前一样,但是我们以后会改变这一点。

神经网络

我们现在将创建我们的神经网络的元素。关于神经网络的快速入门:“人工神经网络(ANN)是一个被称为人工神经元的连接单元或节点的集合,它松散地模拟了生物大脑中的神经元。每个连接,就像生物大脑中的突触,可以向其他神经元传递信号。人工神经元接收信号,然后进行处理,并向与之相连的神经元发出信号。”

这就是我们的未来,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

神经网络

上图显示了一个神经网络,它有一个输入层、两个“隐藏”层(第 2 层和第 3 层)和一个输出层。该模型提供从观察空间到输入层的输入,这些输入被“前馈”到后续层,直到输出层,其中输出层中的值被用于选择动作。

例如,层 3 中的每个节点都是通过激活函数传递的层 2 的线性组合(加权和)。用于计算第 3 层的权重在矩阵中随机初始化,并通过称为 【随机梯度下降】 的过程逐渐调整,以更好地预测输出。激活函数是一个简单的非线性函数,它允许分类器学习基础观察空间中的非线性规则。

在我们的 CartPole 问题中,有 5 个输入(观察空间的元素+一个偏差项)和 2 个输出(我们可以推车的两个方向)。

神经网络层将被封装在一个 NNLayer 类中,

**class NNLayer:**
    # class representing a neural net layer
    def **__init__**(self, input_size, output_size, activation=None, lr       = 0.001):
        self.***input_size*** = input_size
        self.***output_size*** = output_size
        self.**weights** = np.random.uniform(low=-0.5, high=0.5, size=(input_size, output_size))
        self.***activation_function*** = activation
        self.***lr*** = lr

这个类包含三个主要内容:

  1. 图层的尺寸(输入和输出尺寸)。
  2. 连接输入图层和输出图层的权重。
  3. 输出的激活函数(默认激活为无,也称为线性)。

我们现在将这个类的用法添加到我们的 RLAgent 中。首先,我们将编辑选择动作函数,

def **select_action**(self, observation):
        values = self.forward(observation)
        if (np.random.random() > self.epsilon):
            return np.argmax(values)
        else:
            return np.random.randint(self.env.action_space.n)

这个函数不是每次都随机选择一个值,而是将关于环境状态的信息传递给我们的神经网络,并计算每个行为的“预期回报”(values 是一个接受(1,nᵢₙ)数组并返回(1,nₒᵤₜ)数组的函数)。然后,它会选择能带来最大预期回报的行动。注意,该函数仍然选择概率为ε的随机值。ε,也称为“探索率”,是强化学习中一个重要概念的实现:探索和利用之间的权衡。探索有助于模型不陷入局部最小值,通过不时探索明显次优的行动,可能揭示更大的回报。另一方面,利用允许代理使用其对当前状态的了解来选择最有利可图的动作。在大多数 RL 代理中,epsilon 在开始时很高(接近 1.0),并且随着时间的推移逐渐降低到 0,因为代理在给定状态下对动作的学习值变得更有信心。

select_action 函数还调用 self.forward (RLAgent。Forward),下面是这个函数的代码,

def **forward**(self, observation, remember_for_backprop=True):
        vals = np.copy(observation)
        index = 0
        for layer in self.layers:
            vals = layer.forward(vals, remember_for_backprop)
            index = index + 1
        return vals

上面的 RLAgent.forward 函数有一个简单的循环。它将输入(我们试图决定行动过程的观察)传递给网络,并获得每个行动的一组值。它在内部调用 NNLayer.forward 函数,收集每一层的输出并将其传递给下一层。为了完成 select action 函数的实现,下面是最后一部分 NNLayer.forward 函数。remember_for_backprop 参数是一个布尔值,它指定是否需要存储某些计算值,以防止权重更新期间的重复计算(这将在反向传播一节中详细解释)。

# Compute the forward pass for this layer
def **forward**(self, inputs, remember_for_backprop=True):
        input_with_bias = np.append(np.ones((len(inputs),1)),inputs, axis=1)
        unactivated = np.dot(input_with_bias, self.weights)
        output = unactivated
        if self.***activation_function*** != None:
            output = self.***activation_function***(output)
        if remember_for_backprop:
            # store variables for backward pass     
            self.***backward_store_in*** = input_with_bias
            self.***backward_store_out*** = np.copy(unactivated)

        return output

这个功能—

  1. 将偏差项追加到输入中。
  2. 计算该层的输入和权重矩阵的乘积。
  3. 获取步骤 2 的输出,如果已经为该层定义了一个激活函数(在我们的例子中是 ReLU),则通过该函数发送这个输出。

让我们在 RLAgent 的 init 函数中添加这些层的实例化,

def **__init__**(self, env):
   self.***env*** = env
   self.***hidden_size*** = 24
   self.***input_size*** = env.observation_space.shape[0]
   self.***output_size*** = env.action_space.n
   self.***num_hidden_layers*** = 2
   self.***epsilon*** = 1.0 self.***layers*** = [NNLayer(self.input_size + 1, self.hidden_size,       activation=relu)]
   for i in range(self.num_hidden_layers-1):
       self.***layers***.append(NNLayer(self.hidden_size+1, self.hidden_size, activation=relu))
   self.***layers***.append(NNLayer(self.hidden_size+1, self.output_size))

你可以看到上面我们总共有 2 个隐藏层和 1 个输出层。此外,在除了输出层之外的所有层中,我们都使用了一个激活函数,叫做RectivedLlinearUnit(ReLU)。这个函数是一个非常简单的函数,它给我们的神经网络引入了足够的非线性。这是它的实现,

def **relu**(mat):
    return np.multiply(mat,(mat>0))

此函数接收一个矩阵,并返回另一个具有相同值的矩阵,其中原始矩阵大于 0,所有其他值都为 0。最后,让我们将代理的初始化和 epsilon decay 添加到主程序循环中。这是新的主程序的样子:

# Global variables
NUM_EPISODES = 10
MAX_TIMESTEPS = 1000
model = RLAgent(env)# The main program loop
for i_episode in range(NUM_EPISODES):
    observation = env.reset()
    # Iterating through time steps within an episode
    for t in range(MAX_TIMESTEPS):
        env.render()
        action = model.select_action(observation)
        observation, reward, done, info = env.step(action)
        # epsilon decay
        model.epsilon = model.epsilon if model.epsilon < 0.01 else model.epsilon*0.995
        if done:
            # If the pole has tipped over, end this episode
            print('Episode {} ended after {} timesteps, current exploration is {}'.format(i_episode, t+1,model.epsilon))
            break

这个模型目前做什么?它随机初始化神经网络的权重,并基于这些权重计算任何给定状态下的动作值。然而,我们需要一种方法来改进网络中的权重值,以便代理能够在任何给定的状态下采取最佳行动。如前所述,这是通过随机梯度下降实现的,并通过称为反向传播的技术实施。我将在下一篇文章中详细介绍反向传播以及强化学习的相关理论。

总而言之,这就是我们迄今为止所取得的成就:

  1. 编写了与 OpenAI gym 中的 CartPole 的环境空间进行交互的主要程序组件。
  2. 将强化学习代理及其组件神经网络层封装在各自的类中。
  3. 为我们的深度学习强化学习代理编码和初始化神经网络架构。
  4. 实施“前馈”计算,通过神经网络传播对环境的观察,以计算行动值。

在下一篇文章中,我们将致力于实现以下目标:

  1. 检查并形式化一个“累积奖励”的概念,即一个代理人在特定的状态下对掷球问题的期望。
  2. 理解代理应如何更新神经网络中的权重,以更接近其想法,即在特定状态下采取特定行动预期的正确累积回报。
  3. 实现反向传播——实现上面 2 中提到的目标的算法。

下次见!

使用 Tensorflow 从头开始创建更深的瓶颈 ResNet

原文:https://towardsdatascience.com/creating-deeper-bottleneck-resnet-from-scratch-using-tensorflow-93e11ff7eb02?source=collection_archive---------13-----------------------

我们将看到如何使用 Tensorflow 2.0 从头开始实现 ResNet50

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一。剩余块和跳过连接(来源:图片由作者创建)

可以看出,通常较深的神经网络比浅的神经网络性能更好。但是,深度神经网络面临一个共同的问题,称为“消失/爆炸梯度问题”。为了克服这个问题,提出了 ResNet 网络。

原文链接—https://arxiv.org/abs/1512.03385

剩余块:

ResNets 包含剩余块。如图 1 所示,存在激活‘al’,随后是具有 ReLU 非线性的线性层,‘al+1’。接着是另一个线性层,带有另一个非线性,“a l+2 ”。这是正常或普通神经网络的样子。ResNet 增加了跳过连接。在 ResNet 中,来自‘al的信息被快进并复制到‘al+1之后的线性层之后,ReLU 非线性之前。现在具有跳跃连接的整个块被称为残余块。图 2 显示了 ResNet 原始论文中看到的残余块。跳跃连接有助于跳过几乎两层,将其信息传递到神经网络的更深处。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图二。ResNet 论文中显示的残余块(来源:ResNet 论文原文)

使用残余块可以训练更深层次的神经网络。因此,ResNet 论文的作者将这些残差块一个接一个地堆叠起来,形成一个深度残差神经网络,如图 3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3。34 层平面网络与 34 层残差网络的图像片段(来源:原始 ResNet 论文)

可以看出,当使用诸如 SGD 的优化算法来训练深度平面网络时,理论上训练误差应该随着神经网络中层数的增加而不断减小,但实际情况并非如此。在减少到某一层之后,训练误差又开始增加。但是使用残余块可以克服这个问题,并且即使当层数显著增加时,训练误差也保持减小。这可以在图 4 中看到。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。普通网络与 ResNet 的训练误差。X 轴表示层数的增加,y 轴表示训练误差(来源:图片由作者提供)

更深的瓶颈 ResNet 架构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5。普通残余块 vs 更深的瓶颈残余块(来源:原 ResNet 论文)

图 5 显示了普通剩余块与更深瓶颈剩余块之间的差异。在公共残差块中,有两个具有 3×3 滤波器的卷积层。在瓶颈架构中,有 3 个卷积层,而不是 2 个。这三层是 1x1、3x3 和 1x1 卷积,其中 1x1 层负责减少然后增加(恢复)维度,而 3x3 层则成为具有较小输入/输出维度的瓶颈。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6。瓶颈残余块—身份块(来源:图片由作者创建)

如图 6 所示,瓶颈残差块有一个 1x1 的 Conv 层,然后是批规范化和 ReLU 激活,接着是一个 3x3 的 Conv 层,然后是批规范化和 ReLU,最后是一个 1x1 的 Conv 层和一个批规范化。这连接到跳过连接输入,然后应用最终 ReLU 激活。这是第一个瓶颈层版本(身份块)。“f”代表每个 Conv 层中滤光器的数量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7。瓶颈剩余块—投影版本(来源:图片由作者创建)

瓶颈层的第二个版本(投影)与第一个版本非常相似,除了它有一个额外的 1x1 Conv 层,然后是在跳过连接上出现的批量归一化。跳过连接上的额外 Conv 层确保残差块左侧的滤波器数量与残差块右侧的滤波器数量相同。这允许将输入与残差块相加,而没有任何误差。此外,如果我们需要将添加到左侧 Conv 层以减小图像的大小,我们可以通过在跳过连接中在 Conv 层上添加类似数量的步来确保来自前一层的输入也具有相同的大小。

使用 Tensorflow 创建 ResNet50】

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8。我们将创建一个 50 层的 ResNet。图像显示了网络中使用的所有块(来源:原始 ResNet 文件)

  • 在 ResNet 论文中,提到了在每个卷积块之后和激活函数之前使用批量归一化。
  • 该网络始于 Conv 层,其具有 7×7 的核形状和 64 个核(滤波器),步长为 2。随后是批量规范化和 ReLU 激活。
  • 接下来是最大 3x3 的池层和步幅 2。
  • 其后是 ResNet(剩余/瓶颈)块。有 4 个 ResNet 块,每个块中的内核数量和内核大小如图 8 所示。
  • 最后,有一个平均池层,一个完全连接的层,然后是 Softmax 激活。

算法:

  1. 从 Tensorflow 导入所有必要的层
  2. 为 conv-巴奇诺姆-雷卢块写一个函数
  3. 为身份瓶颈块编写一个函数
  4. 为投影瓶颈块编写一个函数
  5. 为 ResNet 块编写一个函数
  6. 一起使用所有函数来创建模型

导入 TensorFlow 和所有必需的库。

*#import the libraries*

**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization
**from** **tensorflow.keras.layers** **import** MaxPool2D, GlobalAvgPool2D
**from** **tensorflow.keras.layers** **import** Add, ReLU, Dense
**from** **tensorflow.keras** **import** Model

创建 conv-巴奇诺姆-雷鲁块的函数

这是基本的构建模块。它包含一个卷积层,随后是 BatchNormalization,接着是 ReLU 激活功能。

*#Conv-BatchNorm-ReLU block*

**def** conv_batchnorm_relu(x, filters, kernel_size, strides=1):

    x = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding = 'same')(x)
    x = BatchNormalization()(x)
    x = ReLU()(x)

    **return** x

功能创建身份瓶颈块

正如我们前面所看到的,一个身份块包含 3 个卷积层,每一层之后是批量标准化和 ReLU 激活,除了最后一层,它首先添加到跳过连接,然后才应用 ReLU 激活。

此外,前两个卷积层的滤波器数量相同,但最后一个卷积层的滤波器数量是原来的 4 倍。

如图 8 所示,“conv2_x”的前两个卷积模块有 64 个滤波器,最后一个卷积模块有 64*4=256 个滤波器。对所有的标识块重复同样的操作。

*#Identity block*

**def** identity_block(tensor, filters):

    x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=1)
    x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
    x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
    x = BatchNormalization()(x)

    x = Add()([tensor,x])    *#skip connection*
    x = ReLU()(x)

    **return** x

创建投影块的功能

投影块的左侧与标识块相似,只是步数不同。

投影块的右侧包含 1×1 卷积层,其滤波器数量是左侧第一层的 4 倍,跨距数量相同。这确保了原始输入和跳过连接具有相同数量的滤波器和相同的步距,否则矩阵加法将会出错。

*#Projection block*

**def** projection_block(tensor, filters, strides):

    *#left stream*
    x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=strides)
    x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
    x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
    x = BatchNormalization()(x)

    *#right stream*
    shortcut = Conv2D(filters=4*filters, kernel_size=1, strides=strides)(tensor)
    shortcut = BatchNormalization()(shortcut)

    x = Add()([shortcut,x])    *#skip connection*
    x = ReLU()(x)

    **return** x

创建 ResNet 块的函数

如图 8 所示,每个模块“conv2_x”、“conv3_x”等都重复多次。“conv2_x”重复 3 次,“conv3_x”重复 4 次,“conv4_x”重复 6 次,“conv5_x”重复 3 次。

在每种情况下,第一个块是投影块,其余的是单位块。所以对于‘con v2 _ x’,第一个块是投影块,另外两个重复是单位块。

第一块是投影的原因是为了确保来自跳过连接的输入和该块的实际输出的步长和滤波器数量是相同的。在投影块中,第一卷积层的跨距=2。这意味着,如果输入的是一个图像,该层之后的图像的大小将会减小。但是跳过连接中的输入仍然具有先前的图像大小。添加两个不同大小的图像是不可能的。所以 skip 连接也有一个 stride=2 的卷积层。这确保了图像尺寸现在是相同的。

在下面的重复中,步幅被设置为 1,因此图像大小保持不变,并且不需要投影块。因此,除了每个 ResNet 块的第一次重复之外,所有其他重复都使用相同的块。

*#Resnet block*

**def** resnet_block(x, filters, reps, strides):

    x = projection_block(x, filters, strides)
    **for** _ **in** range(reps-1):
        x = identity_block(x,filters)

    **return** x

创建模型

既然所有的块都准备好了,那么现在可以按照图 8 创建模型了。

*#Model*

input = Input(shape=(224,224,3))

x = conv_batchnorm_relu(input, filters=64, kernel_size=7, strides=2)
x = MaxPool2D(pool_size = 3, strides =2)(x)
x = resnet_block(x, filters=64, reps =3, strides=1)
x = resnet_block(x, filters=128, reps =4, strides=2)
x = resnet_block(x, filters=256, reps =6, strides=2)
x = resnet_block(x, filters=512, reps =3, strides=2)
x = GlobalAvgPool2D()(x)

output = Dense(1000, activation ='softmax')(x)

model = Model(inputs=input, outputs=output)
model.summary()

输出片段:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘制模型

**from** **tensorflow.python.keras.utils.vis_utils** **import** model_to_dot
**from** **IPython.display** **import** SVG
**import** **pydot**
**import** **graphviz**

SVG(model_to_dot(model, show_shapes=**True**, show_layer_names=**True**, rankdir='TB',expand_nested=**False**, dpi=60, subgraph=**False**).create(prog='dot',format='svg'))

输出片段:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Tensorflow 从头开始创建 ResNet 50 的完整代码:

***#import the libraries***

**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization
**from** **tensorflow.keras.layers** **import** MaxPool2D, GlobalAvgPool2D
**from** **tensorflow.keras.layers** **import** Add, ReLU, Dense
**from** **tensorflow.keras** **import** Model***#Conv-BatchNorm-ReLU block*****def** conv_batchnorm_relu(x, filters, kernel_size, strides=1):
    x = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding = 'same')(x)
    x = BatchNormalization()(x)
    x = ReLU()(x)
    **return** x***#Identity block*****def** identity_block(tensor, filters):
    x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=1)
    x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)
    x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)
    x = BatchNormalization()(x)
    x = Add()([tensor,x])    *#skip connection*
    x = ReLU()(x)
    **return** x
***#Projection block*** 

**def** projection_block(tensor, filters, strides): 

     *#left stream*     
     x = conv_batchnorm_relu(tensor, filters=filters, kernel_size=1, strides=strides)     
     x = conv_batchnorm_relu(x, filters=filters, kernel_size=3, strides=1)     
     x = Conv2D(filters=4*filters, kernel_size=1, strides=1)(x)     
     x = BatchNormalization()(x) 

     *#right stream*     
     shortcut = Conv2D(filters=4*filters, kernel_size=1, strides=strides)(tensor)     
     shortcut = BatchNormalization()(shortcut)          
     x = Add()([shortcut,x])    *#skip connection*     
     x = ReLU()(x)          
     **return** x***#Resnet block***

**def** resnet_block(x, filters, reps, strides):

    x = projection_block(x, filters, strides)
    **for** _ **in** range(reps-1):
        x = identity_block(x,filters)
    **return** x***#Model***

input = Input(shape=(224,224,3))

x = conv_batchnorm_relu(input, filters=64, kernel_size=7, strides=2)
x = MaxPool2D(pool_size = 3, strides =2)(x)
x = resnet_block(x, filters=64, reps =3, strides=1)
x = resnet_block(x, filters=128, reps =4, strides=2)
x = resnet_block(x, filters=256, reps =6, strides=2)
x = resnet_block(x, filters=512, reps =3, strides=2)
x = GlobalAvgPool2D()(x)

output = Dense(1000, activation ='softmax')(x)

model = Model(inputs=input, outputs=output)
model.summary()

参考文献:

  1. 何、、任、,深度残差学习用于图像识别,2015, arXiv:1512.03385【cs .简历]

使用 TensorFlow 创建 DenseNet 121

原文:https://towardsdatascience.com/creating-densenet-121-with-tensorflow-edbc08a956d8?source=collection_archive---------4-----------------------

在本文中,我们将了解如何使用 Tensorflow 从头开始创建 DenseNet 121 架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:DenseNet 中的各种块和层(来源:dense net 原始文件)

DenseNet 论文链接:https://arxiv.org/pdf/1608.06993.pdf

DenseNet(密集卷积网络)是一种架构,专注于通过使用层间更短的连接,使深度学习网络更深入,但同时使它们更有效地训练。DenseNet 是一个卷积神经网络,其中每一层都连接到网络中更深层的所有其他层,即第一层连接到第二层、第三层、第四层等等,第二层连接到第三层、第四层、第五层等等。这样做是为了实现网络层之间的最大信息流。为了保持前馈性质,每一层从所有前面的层获得输入,并将它自己的特征映射传递给它后面的所有层。与 Resnets 不同,它不是通过求和来组合特征,而是通过连接它们来组合特征。因此“第 I”层具有“I”个输入,并且由所有其前面的卷积块的特征图组成。它自己的特征地图被传递给所有下一个“I-i”层。这在网络中引入了’(I(I+1))/2 ‘连接,而不是像传统深度学习架构中那样仅仅是’ I '连接。因此,它比传统的卷积神经网络需要更少的参数,因为不需要学习不重要的特征映射。

DenseNet 由两个重要的模块组成,而不是基本的卷积层和池层。它们是致密块体和过渡层。

接下来,我们看看所有这些块和层是什么样子,以及如何用 python 实现它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 DenseNet121 框架(来源:原始 DenseNet 论文,由作者编辑)

DenseNet 从一个基本的卷积和池层开始。然后是一个密集块后面跟着一个过渡层,另一个密集块后面跟着一个过渡层,另一个密集块后面跟着一个过渡层,最后是一个密集块后面跟着一个分类层。

第一卷积块有 64 个大小为 7×7 的滤波器,跨距为 2。接下来是最大池层,最大池为 3×3,跨距为 2。这两行可以用 python 中的以下代码来表示。

input = Input (input_shape)
x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
x = MaxPool2D(3, strides = 2, padding = 'same')(x)

定义卷积块 —输入后的每个卷积块有如下顺序:BatchNormalization,后面是 ReLU activation,然后是实际的 Conv2D 层。为了实现这一点,我们可以编写下面的函数。

*#batch norm + relu + conv*
**def** bn_rl_conv(x,filters,kernel=1,strides=1):

    x = BatchNormalization()(x)
    x = ReLU()(x)
    x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
    **return** x

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3。密集块(来源:DenseNet 纸-由作者编辑)

定义密集块 —如图 3 所示,每个密集块都有两个卷积,内核大小分别为 1x1 和 3x3。在密集块 1 中,重复 6 次,在密集块 2 中重复 12 次,在密集块 3 中重复 24 次,最后在密集块 4 中重复 16 次。

在密集块中,每个 1x1 卷积具有 4 倍数量的过滤器。所以我们使用 4 *滤镜,但是 3 * 3 滤镜只出现一次。此外,我们必须连接输入和输出张量。

使用“for 循环”,每个模块分别重复 6、12、24、16 次。

**def** dense_block(x, repetition):

   **for** _ **in** range(repetition):
        y = bn_rl_conv(x, 4*filters)
        y = bn_rl_conv(y, filters, 3)
        x = concatenate([y,x])
   **return** x

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4:过渡层(来源:DenseNet 论文-作者编辑)

定义过渡层 —在过渡层,我们要将通道数量减少到现有通道的一半。有一个 1x1 卷积层和一个 2x2 平均池层,步长为 2。在函数 bn_rl_conv 中已经设置了 1x1 的内核大小,所以我们不需要明确地再次定义它。

在过渡层中,我们必须将通道减少到现有通道的一半。我们有输入张量 x,我们想知道有多少个通道,我们需要得到其中的一半。因此,我们可以使用 Keras backend (K)获取张量 x,并返回一个维度为 x 的元组。我们只需要该形状的最后一个数字,即过滤器的数量。所以我们加[-1]。最后,我们可以将这个数量的滤波器除以 2,得到想要的结果。

**def** transition_layer(x):

    x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
    x = AvgPool2D(2, strides = 2, padding = 'same')(x)
    **return** x

我们已经完成了密集数据块和过渡层的定义。现在我们需要将密集的块和过渡层堆叠在一起。所以我们写了一个 for 循环来运行 6,12,24,16 次重复。因此循环运行 4 次,每次使用 6、12、24 或 16 中的一个值。这完成了 4 个密集块和过渡层。

**for** repetition **in** [6,12,24,16]:

    d = dense_block(x, repetition)
    x = transition_layer(d)

最后是 GlobalAveragePooling,接下来是最终的输出层。我们在上面的代码块中看到,密集块是用‘d’定义的,在最后一层,密集块 4 之后,没有过渡层 4,而是直接进入分类层。因此,“d”是应用 GlobalAveragePooling 的连接,而不是“x”上的连接。另一种方法是从上面的代码中删除“for”循环,然后一层接一层地堆叠这些层,而没有最后的过渡层。

x = GlobalAveragePooling2D()(d)
output = Dense(n_classes, activation = 'softmax')(x)

现在我们已经把所有的模块放在一起了,让我们把它们合并起来,看看整个 DenseNet 架构。

完成 DenseNet 121 架构:

**import** **tensorflow** **as** **tf**
**from** **tensorflow.keras.layers** **import** Input, Conv2D, BatchNormalization, Dense
**from** **tensorflow.keras.layers** **import** AvgPool2D, GlobalAveragePooling2D, MaxPool2D
**from** **tensorflow.keras.models** **import** Model
**from** **tensorflow.keras.layers** **import** ReLU, concatenate
**import** **tensorflow.keras.backend** **as** **K***# Creating Densenet121***def** densenet(input_shape, n_classes, filters = 32):

    *#batch norm + relu + conv*
    **def** bn_rl_conv(x,filters,kernel=1,strides=1):

        x = BatchNormalization()(x)
        x = ReLU()(x)
        x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
        **return** x

    **def** dense_block(x, repetition):

        **for** _ **in** range(repetition):
            y = bn_rl_conv(x, 4*filters)
            y = bn_rl_conv(y, filters, 3)
            x = concatenate([y,x])
        **return** x

    **def** transition_layer(x):

        x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
        x = AvgPool2D(2, strides = 2, padding = 'same')(x)
        **return** x

    input = Input (input_shape)
    x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
    x = MaxPool2D(3, strides = 2, padding = 'same')(x)

    **for** repetition **in** [6,12,24,16]:

        d = dense_block(x, repetition)
        x = transition_layer(d) x = GlobalAveragePooling2D()(d)
    output = Dense(n_classes, activation = 'softmax')(x)

    model = Model(input, output)
    **return** modelinput_shape = 224, 224, 3
n_classes = 3model = densenet(input_shape,n_classes)
model.summary()

输出:(假设最后 3 节课—模型总结的最后几行)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要查看架构图,可以使用下面的代码。

**from** **tensorflow.python.keras.utils.vis_utils** **import** model_to_dot
**from** **IPython.display** **import** SVG
**import** **pydot**
**import** **graphviz**

SVG(model_to_dot(
    model, show_shapes=**True**, show_layer_names=**True**, rankdir='TB',
    expand_nested=**False**, dpi=60, subgraph=**False**
).create(prog='dot',format='svg'))

输出—图表的前几块

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就是我们实现 DenseNet 121 架构的方式。

参考文献:

  1. 1.黄高和刘庄以及劳伦斯·范德马腾和基利安·q·温伯格,密集连接的卷积网络,arXiv 1608.06993 (2016 年)

创建有效的行业-学术人工智能合作伙伴关系

原文:https://towardsdatascience.com/creating-effective-industry-academic-ai-partnerships-87967aacc8f1?source=collection_archive---------60-----------------------

活动讲座

达林·格雷厄姆| TMLS2019

https://torontomachinelearning.com/

关于演讲者

Darin 在领导创新计划方面拥有 20 多年的经验,这些创新计划将创意推向市场。他在领导 R&D 项目和帮助加拿大、美国、新西兰和英国建立创新生态系统方面拥有丰富的国际经验。

作为合作的倡导者,他与各种独特的团体密切合作,让行业与学术和研究机构合作,以增强基于技术的应用成果,特别是与人工智能(AI)相关的成果,通过增加知识和帮助开发人力资本来促进经济增长。

Darin 刚刚担任了一个新角色,负责 LG 电子的 R&D 战略和运营人工智能活动,在多伦多建立了他们的新实验室。最近,作为创始运营团队的一员,Darin 帮助领导了 Vector Institute 的创建和形成,这是加拿大首屈一指的人工智能研究机构。

他还帮助建立和启动了三星在多伦多的人工智能实验室。他曾在多个组织担任主要领导职务,包括 ORION(安大略省研究和创新光纤网络)、NZi3(新西兰 ICT 创新研究所)和 CITO(安大略省通信和信息技术,安大略省卓越中心)。

Darin 在多伦多大学获得了航空航天工程博士学位,他的论文重点是自主机器人控制系统的高级神经网络;多伦多大学航空航天工程硕士,滑铁卢大学计算机科学和应用数学学士。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建有效的产业-学术人工智能合作伙伴关系

使用机器学习创建面部识别软件

原文:https://towardsdatascience.com/creating-facial-recognition-software-with-deep-learning-b79931f24c5f?source=collection_archive---------56-----------------------

使用 Keras 和 OpenCV

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 pixabay像素上拍摄

虽然用机器学习来进行面部识别的想法很早就被人知道了,但是自己创作这个软件还是挺有用的。

概念:

本文的目标是创建一个二元分类面部识别网络,允许一个人访问,而不允许另一个人访问。

我将使用 open-cv 访问计算机中的内置摄像头,并使用 haar-cascade 分类器来检测图像中的人脸。收集数据后,我会使用卷积神经网络对数据进行模型训练。

之后,当 OpenCV 检测到被授予访问权限的人时,它会在他/她的脸部周围画一个正方形,正方形上有“访问已验证”的字样。

代码:

from numpy import unique
from numpy import argmax
import os
import cv2
from PIL import Image
import numpy as np
from tensorflow.keras import Sequential
from tensorflow.keras import optimizers
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Dropout

除了标准的 numpy 和 os 库用于数据访问和数据操作,我还使用 open-cv 和 PIL 进行图像处理。

def data_path():
    file = 'XXXXXXXX'
    os.chdir(file)
    files = os.listdir()
    files.remove('.DS_Store')
    return file,files

这是访问你计算机中数据的功能。要使此函数工作,请将变量文件更改为数据所在的相关目录。

def data_setup(files,file):
    pixels = [0]*len(files)
    answers = list()
    print(len(files))
    for i in range(len(files)):
        image = Image.open(files[i])
        pixels[i]= np.asarray(image)
        pixels[i] = pixels[i].astype('float32')
        pixels[i] /= 210.0
        if files[i][0] == 'm':
            answers.append(1)
        elif files[i][0] == 'n':
            answers.append(0)
    dataset = np.array(pixels)
    for i in range(len(dataset)):
        dataset[i] = dataset[i].reshape(320,320)
    return np.asarray(dataset),np.asarray(answers)

该函数访问数据,并将所有照片重新整形为 320 x 320 的图片。然后,它返回 X 和 y 值,即数据和真值。

def train(data,answers):
    x_train = data
    y_train = answers
    x_train = np.array(x_train)
    x_train = x_train.reshape((x_train.shape[0], x_train.shape[1], x_train.shape[2], 1))
    print(x_train.shape)
    in_shape = x_train.shape[1:]
    print(len(data),len(answers))
    model = Sequential()
    model.add(Conv2D(10, (3,3), activation='relu', kernel_initializer='he_uniform', input_shape=in_shape))
    model.add(MaxPooling2D((2, 2)))
    model.add(Conv2D(10, (3,3), activation='relu', kernel_initializer='he_uniform', input_shape=in_shape))
    model.add(MaxPooling2D((2, 2)))
    model.add(Flatten())
    model.add(Dense(1,activation ='sigmoid'))
    model.compile(optimizer='adam', loss='binary_crossentropy',metrics = ['accuracy'])
    model.fit(x_train, y_train, epochs=100, batch_size=100, verbose = 2, validation_split = 0.33)
    return model

这个脚本创建、编译和训练卷积网络。使用的损失是二进制交叉熵,度量是准确度,因为我们希望网络以高准确度预测正确的人脸。

def face_recognition(model):
    dirx = 'XXXXXXXXXXXXXXXXXXXXXXXXXX'
    os.chdir(dirx)
    face_cascade = cv2.CascadeClassifier('cascades/data/haarcascade_frontalface_alt.xml')
    cap = cv2.VideoCapture(0)
    while True:
        ret,frame = cap.read()
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray,scaleFactor = 1.05, minNeighbors = 5)
        for (x,y,w,h) in faces:
            print('Face Detected')
            roi_gray = gray[y:y+h,x:x+w]
            roi_color = frame[y:y+h,x:x+w]
            roi_gray = roi_gray.astype('float32')
            roi_gray /= 210.0
            classify = cv2.resize(roi_gray,(320,320))
            if classify.shape == (320,320):
                classify = classify.reshape((1, classify.shape[0], classify.shape[1], 1))
                color = (255,0,0)
                stroke = 2
                end_cord_x = x+w
                end_cord_y = y+h
                pred = model.predict(classify)
                print(pred)
                if pred == 1:
                    cv2.rectangle(frame,(x,y),(end_cord_x,end_cord_y),color,stroke)
                    cv2.putText(frame, 'Access', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
                elif pred == 0:
                    cv2.rectangle(frame,(x,y),(end_cord_x,end_cord_y),color,stroke)
                    cv2.putText(frame, 'Denied Access', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
        if cv2.waitKey(20) & 0xFF == ord('q'):
            break
        cv2.imshow('frame',frame)

该脚本应用模型并实时进行预测,创建并标记有面的盒子。要使这个函数工作,您必须再次确定 haar-cascade 在哪个目录中。您可能需要将文件夹从外部位置移到正确的目录。

file,files=data_path()
data,answers = data_setup(files,file)
model = train(data,answers)
face_recognition(model)

程序的最后一部分操作所有功能,并启动实时面部识别。

如何改进我的计划:

当我写程序时,我总是分享一个强大的框架,在那里可以添加更复杂的功能。以下是一些可以增加程序功能的方法:

  • 多类分类

当给定每个人的一组平衡的照片时,尝试训练模型来预测这个人是谁,而不是二进制分类。

  • 添加更多的哈尔级联分类器

我使用的 haar-cascade 是正面人脸 cascade,只能检测正面人脸照片。你可以添加更多的分类器,这将使它工作,不管相机的角度。

我的链接:

如果你想看更多我的内容,点击这个 链接

用 OpenAI 的语言模型生成假新闻

原文:https://towardsdatascience.com/creating-fake-news-with-openais-language-models-368e01a698a3?source=collection_archive---------58-----------------------

GPT 和 NLP 时代的种族和政治

简介

在我们之前的文章中,我们讨论了最近发布的 OpenAI 的 GPT-3 及其在误传时代可能的影响。我们讨论了 GPT 系列的发展、架构和历史,并评估了在原始 出版物中呈现的一些功能和结果。

然而,由于这些是研究小组选择的文献实例,它们不具备使它们与当今紧迫主题相关的必要背景。虽然 GPT-3 尚未向公众发布,但被作者称为“太危险而不能发布”的 GPT-2 已于 2019 年初推出。因此,它为我们的问题陈述充当了这种模型的能力的一个伟大的估计者。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GPT-2 模型重量配置。(阿拉玛等人。艾尔

在我们的上一篇文章中,我们已经介绍了 GPT 系列模型的变压器型架构。最重要的是,该模型以 4 种不同的预设重量配置发布,参数从 1.17 亿延伸到 15 亿。可以想象,模型复杂度的增加伴随着各种 NLP 任务性能的提高。

GPT-2 在 WebText 数据集上接受训练,该数据集由用户在 Reddit 平台上发布的 4500 万个出站链接的文本内容组成。网络文本中访问量排名前 15 位的域名是:谷歌、存档、博客、GitHub、纽约时报、Wordpress、华盛顿邮报、维基、BBC、卫报、易贝、Pastebin、CNN、雅虎!,以及《赫芬顿邮报》。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 GPT-2 原始出版物的问答示例。

该模型在生成今日最新头条的人类可接受内容方面表现如何?该型号在低重量配置下表现如何?我们能证实基于变压器模型的批评者提出的担忧吗?

让我们来看看 GPT-2 在生成媒体中最近一些相关主题的传真输出方面表现如何。

实施

由于从零开始训练 GPT-2 模型的复杂性,我们将采用 GPT 图书馆的 Lopez-Franco 的操场实现来检查使用条件样本生成的输出差异。你会在 GradientCrescent Github 上找到改编后的代码。

具体来说,我们将比较两种不同的 GPT-2 配置,小型模型(1.17 亿个参数)和大型模型(7.75 亿个参数),通过检查在一些相关当代主题上产生的一些输出。请注意,GPT-2 有一个公开的超大模型(有高达 15 亿个参数),但由于它的大小,我们选择忽略它。**

笔记本是高度抽象的,条件样本生成的关键方法位于interactive _ conditional _ samples . py:

!python3 src/interactive_conditional_samples.py — model_name=’117M’ — nsamples=2 — top_k=40 — temperature=.80
  • model_name = '117M':型号选择。有 117 米、345 米、774 米和 1558 米型号可供选择。。
  • seed = None:随机值发生器,用来在将来产生相同的结果。
  • nsamples = 1:指定输出样本的数量。
  • length = None:每个样本要打印的令牌数(字数)。
  • batch_size= 1:你想同时处理多少个输入。
  • temperature = 1:在 0 和 1 之间浮动。在 softmax 层之前进行采样之前,对输出逻辑进行缩放。更高的温度导致更多的随机完井。
  • top_k = 0:控制分集的整数值。截断被认为具有最高值的逻辑集合。1 表示每一步只考虑一个单词(标记),导致确定性完成。40 表示每一步考虑 40 个单词。0(默认值)是一个特殊设置,表示没有限制。

这个脚本是用 Tensorflow Core 编写的,它接受用户输入文本,将它们编码成标记化的表示形式,然后将它们输入到模型中,在指定的约束条件下生成输出。

with tf.Session(graph=tf.Graph()) as sess:
 context = tf.placeholder(tf.int32, [batch_size, None])
 np.random.seed(seed)
 tf.set_random_seed(seed)
 output = sample.sample_sequence(
   hparams=hparams, length=length,
   context=context,
   batch_size=batch_size,
   temperature=temperature, top_k=top_k, top_p=top_p
  )saver = tf.train.Saver()
 ckpt = tf.train.latest_checkpoint(os.path.join(models_dir, model_name))
 saver.restore(sess, ckpt)while True:
 raw_text = input(“Model prompt >>> “)
 while not raw_text:
   print(‘Prompt should not be empty!’)
   raw_text = input(“Model prompt >>> “)
 context_tokens = enc.encode(raw_text)
 generated = 0
 for _ in range(nsamples // batch_size):
   out = sess.run(output, feed_dict={
   context: [context_tokens for _ in range(batch_size)]
   })[:, len(context_tokens):]
   for i in range(batch_size):
     generated += 1
     text = enc.decode(out[i])
     print(“=” * 40 + “ SAMPLE “ + str(generated) + “ “ + “=” * 40)
     print(text)
 print(“=” * 80)

由于该模型是在一年多前定型的,因此最近的主题(如最近的新冠肺炎疫情)不会出现在数据集中。对所有生成的样本进行了系统的交叉引用,得出的结论是,这些句子没有一个是逐字逐句从某篇文章中复制来的。所有样本的内容都是合成的,而不是记忆的。

我们来看一些话题。

论特朗普总统

775 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

117 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然在这两个例子中,整体的流程和语法看起来很自然,但是两个模型都显示出不一致,这些问题在较小的模型中更加明显。

特朗普表示,“非美国”媒体正试图转移他所谓的“我们历史上最大的问题”,即美国和英国的“非美国”媒体。

“这家媒体,以色列的媒体,沙特阿拉伯和其他许多国家的媒体,试图把我们所有人都关进监狱,我的意思是,这是一种耻辱。我的意思是,我们在监狱里,”特朗普说。

  • 美国传统中东盟友的所有媒体都试图“把他们关进监狱”是不太可能的。不幸的是,由于总统倾向于重复自己的话,区分真实和虚构变得更加困难。然而,大量的重复引起了怀疑。

大模型的文本要连贯得多,除了一句话:

特朗普曾表示,朝鲜应该在 2022 年之前成为“核大国”。

  • 这将与美国对朝鲜的外交政策背道而驰。虽然这句话确实有道理,但它没有抓住朝鲜和全球对其核计划的看法之间的隐含关系。

关于奥巴马总统

775 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

117 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然较大模型的输出对于人类输出来说似乎还过得去(除非有人被国税局杀害的指控),但较小模型的输出虽然语法正确,但表现出多个主题不一致:

曾在竞选中承诺“废除并取代”奥巴马医改的奥巴马表示,共和党的税收计划将通过减税 10%来伤害美国人。

  • 减税通常被认为不会伤害美国人。

根据凯撒家庭基金会(Kaiser Family Foundation)的一项研究,该法案将影响 140 万居住在美国的人,高于前一年的 220 万。

  • 受该计划影响的人数减少了,而不是增加了,所以从开始的一词是不正确的。

共和党人还表示,该计划将在 2030 年前减少 1300 亿美元的联邦赤字

  • 减税不太可能减少联邦赤字。

但白宫表示,该法案不会改变该计划的核心税收结构,只会降低年收入超过 25 万美元的人的利率和所得税。

  • 白宫减税的收入门槛作为一个可行的税收计划肯定很难实现。

论警察暴行

775 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

117 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这两种配置之间的差异在这里更加明显,较小模型的输出几乎是无意义的。

这导致了近年来广泛的警察暴力行为,导致了 700 多人在收缴警察枪支时被捕。

  • 警察暴力不太可能导致“警察的枪被拿掉”。可能是模型学习了从某人手中拿枪的概念,在这里犯了一个语法错误。

警察局长比尔·布拉顿(Bill Bratton)此前表示,他担心人们会因为最近一连串的枪击事件而离开警察队伍。他补充说,他担心他们将来可能会被警察开枪打死,他希望看到那些不在警察队伍中的人被开枪打死,以便那些不在服务中的人留在警察队伍中。

  • 警察局长布拉顿的声明在这里是完全荒谬的。警察怎么会希望看到已经在枪击中丧生的人,或者担心前警察将来会被警察枪杀?

但当案件最终被提交到大陪审团时,大陪审团尚未决定是否起诉该男子,该案件目前正由 NYPD 调查。

纽约警察局一再声明,它从未见过以这种方式实施的“毒品相关或暴力犯罪”案件。

  • 很可能 NYPD 在这个案子被带到大陪审团之前就已经在调查了。此外,没有详细说明犯罪的方式。

上黑人的命也是命

775 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

117 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

大模型的输出对于人类输出来说还过得去,除了集会组织者反复提到的。相比之下,较小的模型显示不一致。

警方被指控在 8 月份杀害罗德尼·金的事件中行为残忍,而金近年来在全国范围内一直颠倒黑白。但在当今世界,一名警官有一个暴力、虐待或以其他方式虐待的伴侣并不罕见,媒体通常不愿意报道这些案件。

  • 罗德尼·金被杀的悲剧案件是警察暴行的一个很好的例子,但是“黑白颠倒”这句话没有语法意义。此外,最后一段提出了两个看似不相关的案例,这并没有增加论点,似乎不合适。

关于系统性种族主义

775 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

117 米

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两个模型都显示出一些不一致。对于更大的模型,托马斯的评论是重复的,并且是不一致的。

托马斯一直是黑人的命也是命运动的倡导者,他认为警察经常成为暴力和种族主义言论的目标。

“他们觉得警方花的时间太长了,而且他们在执法方面做得不够。”

  • 警察不太可能认为他们反应迟钝,做得不够。相反,少数民族被指定为种族主义的目标更有意义,这一点没有得到正确的推断。

较小的模型表现出明显更多的不一致性,标点符号也很差。

已故的迈克尔·b·威廉姆斯(Michael B. Williams)在他的新书《我们是谁》(Who We Are)中认为,美国的“种族鸿沟”不是一个“种族”问题。这不是阶级或种族的问题。换句话说,正如威廉姆斯在他的《种族与性别》中所写的,“美国是一个种族体系。

  • 虽然可以推断出作者论点的主旨,但种族的重复是多余的。此外,在声称种族划分与种族概念无关后,宣布美国是“一个种族系统”是没有意义的。

结论

很明显,随着 GPT-2 模型复杂性的增加,我们观察到产生涵盖当今媒体头条主题的人工生成内容的能力增加。

相比之下,据报道最大的 GPT-3 模型拥有超过 1750 亿个参数,其性能与其复杂性相匹配。这种模型的扩散的影响,特别是当结合对特定情绪或色调的数据进行微调时,将产生模型,这些模型不仅能够生成特定主题的内容,还能够生成特定论点的内容。这将产生潜在的破坏性后果,因为它能够通过为期望的论点提供现实的来源来改变话语的背景。

幸运的是,这些模型的复杂性和资源密集性将限制其使用,目前只有资源极其丰富的组织,如国家级行为者。最终,解决“假新闻”危险的方法不在于简单地试图限制语言模型的扩散,而在于提高我们作为一个社会的信息素养和批判性思维的技能。

这就结束了我们对变压器类型模型的讨论。下一次,我们回到强化学习,并在 Pytorch 中实现价值学习,以进一步解决我们的代理在 Doom 中的表现。

我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库。

参考

[## 更好的语言模型及其含义

我们已经训练了一个大规模的无监督语言模型,它可以生成连贯的文本段落,实现…

openai.com](https://openai.com/blog/better-language-models/) [## 用无监督学习提高语言理解

我们通过一个可扩展的、与任务无关的系统,在一系列不同的语言任务上获得了最先进的结果…

openai.com](https://openai.com/blog/language-unsupervised/) [## 根据人类偏好微调 GPT-2

我们已经对 774M 参数 GPT-2 语言模型进行了微调,在各种任务中使用了人类反馈,成功地匹配了…

openai.com](https://openai.com/blog/fine-tuning-gpt-2/)

用 Python 创建分形

原文:https://towardsdatascience.com/creating-fractals-with-python-d2b663786da6?source=collection_archive---------2-----------------------

在这个帖子里自己试试吧!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分形艺术— 来源

首先,什么是几何分形?几何分形是一种在不同尺度上具有重复结构的几何形状:无论你是否靠近图像,你都会看到相同的图案。或者,正如伯努瓦·曼德尔布罗所定义的,“一个粗糙或支离破碎的几何形状,它可以被分割成多个部分,每个部分(至少近似地)是整体的缩小版”。

现在,我们如何在 Python 中构建一个分形?假设我们在不同的尺度上重复一个结构,我们将需要应用一个递归解决方案。此外,我们将使用 turtle 来绘制分形。在这篇文章中,我们将同时绘制分形树和科赫雪花。

绘制分形树

为了创建一棵树,我们将每个分支分成两个子分支(左和右),并缩短新的子分支,直到达到我们自己定义的最小分支长度:

import turtleMINIMUM_BRANCH_LENGTH = 5def build_tree(t, branch_length, shorten_by, angle):
  passtree = turtle.Turtle()
tree.hideturtle()
tree.setheading(90)
tree.color('green')build_tree(tree, 50, 5, 30)
turtle.mainloop()

到目前为止,我们只是定义了基础。我们导入了 turtle 并创建了 turtle 的一个实例。Turtle(),它将是在画布上移动并绘制我们的树的对象。然后我们用 setheading() 让它面朝上。我们还定义了递归函数的签名,如下所示:

  • t:我们的海龟实例。
  • branch_length:分支的当前长度,以像素为单位。
  • shorten_by:决定子分支比父分支短多少像素。
  • 角度:子分支从父分支出现的角度。

此外,我们已经定义了 MINIMUM_BRANCH_LENGTH(以像素为单位),它设置了创建更多子分支的最小阈值。

现在让我们构建递归函数的主体:

import turtleMINIMUM_BRANCH_LENGTH = 5def build_tree(t, branch_length, shorten_by, angle):
  if branch_length > MINIMUM_BRANCH_LENGTH:
    t.forward(branch_length)
    new_length = branch_length - shorten_by t.left(angle)
    build_tree(t, new_length, shorten_by, angle) t.right(angle * 2)
    build_tree(t, new_length, shorten_by, angle) t.left(angle)
    t.backward(branch_length)tree = turtle.Turtle()
tree.hideturtle()
tree.setheading(90)
tree.color('green')build_tree(tree, 50, 5, 30)
turtle.mainloop()

如您所见,如果 branch_length 小于 MINIMUM_BRANCH_LENGTH,我们就达到了基本情况。否则,我们绘制分支并继续创建子分支,方法是计算它们的长度,向左和向右旋转“角度”度,并用新值再次调用 build_tree。最后,我们向后移动到我们分支的根。

如果您执行该代码,应该会得到以下结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的分形树

最后,您可以随意使用这里的代码(和参数)!

画科赫雪花

在这篇文章的第二部分,我们将绘制一个更复杂的结构:科赫雪花

首先,我们需要创建一个递归函数来创建科赫曲线,然后我们将连接其中的 3 条曲线来创建一个雪花。让我们从定义递归函数的参数开始:

  • t:我们的海龟实例。
  • iterations:表示列表下方图像中 n 的值(注意,n=0 表示一条平坦的线,这是我们递归函数的基本情况)。
  • 长度:我们当前(子)雪花中每条边的长度。
  • shortening_factor:确定创建新子雪花时边长除以的因子。
  • 角度:确定新边出现的角度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

科赫曲线构建— 来源

一旦我们定义了我们的递归函数的基本结构,我们可能会达到以下几点:

import turtledef koch_curve(t, iterations, length, shortening_factor, angle):
  passt = turtle.Turtle()
t.hideturtle()for i in range(3):
  koch_curve(t, 4, 200, 3, 60)
  t.right(120)turtle.mainloop()

此时,我们只需实现递归函数。如果我们已经达到我们的基本情况,我们将只是画一条线。否则,我们将更新我们的参数(特别是迭代次数和长度)并调用我们的递归函数 4 次。在这些函数调用之间,我们会先向左,然后向右,最后再向左。让我们看看完整的实现是怎样的:

import turtledef koch_curve(t, iterations, length, shortening_factor, angle): if iterations == 0:
    t.forward(length)
  else:
    iterations = iterations - 1
    length = length / shortening_factor koch_curve(t, iterations, length, shortening_factor, angle)
    t.left(angle)
    koch_curve(t, iterations, length, shortening_factor, angle)
    t.right(angle * 2)
    koch_curve(t, iterations, length, shortening_factor, angle)
    t.left(angle)
    koch_curve(t, iterations, length, shortening_factor, angle)t = turtle.Turtle()
t.hideturtle()for i in range(3):
  koch_curve(t, 4, 200, 3, 60)
  t.right(120)turtle.mainloop()

如果您执行上面的代码,您应该会获得以下结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的科赫雪花

同样,您可以随意使用这里的代码(和参数)!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值