TowardsDataScience 博客中文翻译 2019(五十六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

多模态回归——超越 L1 和 L2 损失

原文:https://towardsdatascience.com/anchors-and-multi-bin-loss-for-multi-modal-target-regression-647ea1974617?source=collection_archive---------13-----------------------

深度学习最著名的用例是图像分类,目标是训练神经网络从 N 个预定义的可能性中选择一个。例如,经过训练的神经网络可以从许多类别中辨别出图像块中的对象类型(例如,猫)。在这种简单的情况下,softmax 损失函数将为您提供很好的服务。然而,有时我们必须回归一个连续的目标。这就是事情开始变得复杂的地方。

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

Regression vs Classification (source)

当你面对一个连续的回归目标时,很容易将 L1 或 L2 损失直接应用到原始目标上,看看效果如何。如果一个回归目标是单峰(或者在分布中只有一个明显的峰值,非常像高斯分布),L2 范数应该很有效。然而,如果你的目标是单峰的,你可能会发现 L2 损失产生不良后果。这篇文章旨在讨论回归连续目标的更有原则的方法,特别是在大范围和超越高斯型目标分布的情况下。

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

Unimodal distribution and beyond

为什么 L2 损失在某些情况下会失败?

概率的角度来看,L2 损失假设基础目标分布是高斯分布(因此是单峰的)。最小化 L2 损失或均方误差(MSE)与最大化高斯对数似然相同。L2 损耗促使网络最小化所有模式上的损耗,这导致对任何单一模式的估计可能较差。特别是,在图像重建应用中,使用 L2 损失通常会导致图像模糊。这是由于假设噪声是高斯分布的失败造成的。

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

Failure to use L2 loss to regress a bimodal data distribution

请注意,L1 损失没有更好。L2 损失采用高斯先验,L1 损失采用拉普拉斯先验,这也是一种单峰分布。直观地说,平滑 L1 损失,或称胡伯损失,是 L1 和 L2 损失的组合,也呈现单峰基础分布。

通常,首先可视化回归目标的分布是一个好主意,并考虑除 L2 之外的其他损失函数,它们可以更好地反映和适应目标数据分布。例如,如果您的目标分布是双峰的,一种直观的方法是找到目标属于哪个模式(或“仓”),然后回归模式中心的偏移。

这正是所谓的多面元损失(或混合分类/回归损失,离散/连续损失)所做的。这个损失在 CVPR 2017 论文 使用深度学习和几何 **的 3D 包围盒估计中首次提出用于角度回归。**原上下文是在单目 3D 车辆检测中回归一个[-π,π]范围内的连续方向角。此后,它被广泛应用于三维物体检测中的汽车方向回归,包括仅使用单目图像的方法(如多级融合monosrFQNet )和使用点云的方法(如锥台点网AVOD )。

多仓损失前

由于回归目标的范围很广,以及周期性造成的角度模糊,方位估计可能是回归中最棘手的问题之一。在论文 制作用于视点估计的多任务 CNN(BMVC 2016)为 CNN 渲染:使用用渲染的 3D 模型视图训练的 CNN 在图像中进行视点估计(CVPR 2015) 中,作者总结了几种用于方向预测的方法。这个问题有不同的表述:

  1. 预测 cos(θ)和 sin(θ)
  2. 预测 cos(θ-π/3)、cos(θ)和 cos(θ+π/3)
  3. 利用 softmax 交叉熵损失,直接分类到 n 个离散箱中
  4. 具有加权交叉熵损失的几何结构感知分类(GSA cls)

3 和 4 的主要区别在于,传统的交叉熵只考虑了一个面元(包含地面真相的面元)的预测,而几何感知交叉熵损失考虑了所有面元由地面真相到每个面元中心的距离加权。3 和 4 都只将得分最高的 bin 作为最终预测。

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

Different ways to orientation prediction

原始多面元损失(用于角度回归)

原多面元损失将目标范围离散化,分成 n 个重叠的面元。对于每个箱,神经网络模型估计输出目标位于第 i 个箱内的置信概率 Ci 和需要应用于箱中心以获得输出目标的剩余项。总多仓损失本质上是分类损失项(通常是 softmax)和位置回归项(通常是 L2 或 L1 或平滑 L1 损失)的加权平均值。

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

Multi-bin loss = classification loss + regression loss

请注意,在训练和推断过程中存在差异。在训练期间,覆盖地面真实角度的所有箱被强制估计正确的目标。在推断期间,选择具有最大置信度的仓,并且通过将该仓的估计残差应用于该仓的中心来计算最终输出。

这一思想可以推广到角度回归以外的许多其他情况。关于重叠箱的快速说明:这对均匀分布更重要,但对多模态分布不太重要,因为后者的箱边界上的样本要少得多。

车辆尺寸回归

这个想法可以扩展到许多其他应用,如车辆大小回归。FQNet ( 基本思想是首先对训练数据集进行 k-means 聚类,找出维度的 K 个聚类中心,并将这些聚类中心作为 3D 长方体。在训练期间,回归模块分别输出每个 3D 锚点长方体的置信度和偏移量。在推理过程中,最终的回归结果是置信度最高的锚点长方体加上相应的偏移量。

对于车辆尺寸回归,他们似乎没有使用重叠箱,因为维度分布可以很好地聚集到 k 箱中,并且很少样本位于边界上。

箱子宽度选择

如果回归目标中没有清晰的聚类模式,并且目标范围较宽,也建议使用标准差作为 bin 大小,如 GS3D:一种高效的自动驾驶 3D 对象检测框架 **(CVPR 2019)所推荐的。**据报道,这比直接回归目标要好得多。

改进的多仓损失

3D-RCNN:通过渲染和比较 **(CVPR 2018),**作者提出了不同的多面元损失。它还将连续的目标范围离散化为条柱,但不是使用 softmax 挑选出唯一正确的条柱,而是使用条柱中心的加权平均值(期望值)作为最终预测,并使用 L1 对其进行正则化。以这种方式,不需要将偏移回归到面元中心。

回想起来,这非常接近 GSA(几何结构感知)分类的公式,在推理性能上有一点扭曲。

对象检测中的锚盒

现在让我们转到一个更大的话题,对象检测中的锚盒。目标检测领域的新手可能会对锚盒的想法感到困惑,锚盒在许多现代目标检测网络架构中很流行,例如更快的 RCNN、RetinaNet 和 YOLOv2。几篇无主播的论文里有几个很好的主播总结我想在这里引用一下(你得对某件事有足够的了解才能排除,嗯?):

Anchor 方法建议将盒子空间(包括位置、比例、长宽比)划分为离散的 bin,并在相应的 bin 中细化对象盒子。(来自黄斑盒,https://arxiv.org/abs/1904.03797)

锚盒设计用于将所有可能的实例盒的连续空间离散化为具有预定义位置、比例和纵横比的有限数量的盒。(来自 https://arxiv.org/abs/1903.00621FSAF)

看待 anchor 的一种方式是,它类似于深度学习时代之前的滑动窗口。DL 中的锚盒只是以卷积方式应用滑动窗口的一种方式。

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

Sliding window method for face detection in pre-DL era (from one of my favorite blog PyImageSearch)

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

Anchor boxes in DL is a way to apply the sliding window in a convolutional manner (source)

从另一个角度看,锚箱和多仓亏损背后的哲学极其相似。**锚箱和多仓损失都通过对锚(或多仓损失中的“仓”)进行分类并将残差回归到锚/仓来解决非常复杂的多峰回归问题。**也许对象检测中的锚箱以某种方式启发了多箱损失的产生。

实际上,锚盒被设计成基于训练数据集来捕捉比例和纵横比(如果你想使用非均匀锚平铺,甚至是空间集中度),正如在 YOLOv2 中仔细解释的那样(见下图)。这非常类似于 FQNet 中多箱损失的车辆规模回归中的 k 均值聚类。

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

YOLOv2 explains how to select anchor sizes and aspect ratios based on training data

外卖

  1. 视觉化:在盲目应用 L2 规范之前,总是检查目标分布。
  2. 分而治之:将目标范围分成仓(重叠或不重叠),对目标落入哪个仓/锚进行分类,然后对残差进行回归。

PS,我发现的另一个有用的技巧是执行目标标准化以获得目标的零均值和单位方差。虽然标准化通常用在传统机器学习的特征中,并且对于 target 来说不是必需的,但是我发现它很有用,并且如果我们也执行 target 标准化,会使训练更容易。

这个节目的明星是——PYTHON

原文:https://towardsdatascience.com/and-the-star-of-the-show-is-python-51909c894390?source=collection_archive---------29-----------------------

2019 年十月世界的亮点

介绍

又到了一年中的这个时候,最大的开源项目 Github 发布了它的年度报告。GitHub 每年都会对开发者进行调查,并制作一份调查结果报告。

每年都有令人惊讶的消息传来。它揭示了一些新的成长中的技术,热门话题和项目等。

今年无疑是 Python 之年。

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

今年,Github 社区已经接触了来自世界各地的 4000 多万开发者,并且拥有超过 1 亿个存储库。在这里,我们将看到来自 2019 年十月状态的亮点。

出类拔萃,成为明星

全球团队

为开源项目做出贡献的 GitHub 用户有 20%来自美国,80%来自美国以外。

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

开源社区在美国以外的地区越来越受欢迎。来自中国的开发人员在克隆和分叉项目上取得了 48%的增长。

这是美国以外前 20 个地区的图表

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

从各大洲来看,对开源项目的总体贡献是最大的,其中亚洲的贡献最大,来自中国。

下图显示了各大洲对我们的贡献。

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

互联社区

每个语言生态系统中的前 50 个包都有大量的依赖项目。Javascript 语言的顶级 npm 包有大约 350 万个依赖项目。

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

Top 10 open source projects with most dependent projects

Tensorflow 是 GitHub 上最受欢迎的项目之一,它展示了这些小项目如何连接在一起构建一个大型软件社区。它拥有超过 25,000 名社区贡献者,在这一年中为 TensorFlow 依赖项做出了贡献。

是时候证明自己了

社区趋势

2019 年,开发人员的工作效率比以往任何时候都高。下图显示了开发人员正在从事的顶级项目。

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

Octoverse 2019 最引人注目的事实是 Python 第一次超过了 Java ,成为 GitHub 上第二大最受欢迎的语言,基于知识库贡献者。除此之外,Javascript 照常戴着皇冠。

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

Most Popular Programming Languages on GitHub

像深度学习、机器学习、自然语言处理这样的主题已经变得很流行,它们在知识库中也很流行。

该图显示了数据科学 Jupyter 笔记本和自然语言处理中存储库的增长。

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

今年我们看到了语言发展的新趋势。该报告向我们展示了增长最快的 10 种编程语言。Dart 正以 532%的惊人速度增长。开发者社区喜欢统计类型语言,比如 Rust、Kotlin 和 Typescript。

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

包扎

每年来自 GitHub 的见解都有助于开发者分析新趋势和获取信息。今年,我们观察了开源社区是如何由全球开发者构建的,顶级开源项目依赖于数百万个其他项目,python 取代了 java。IT 行业是一个快速增长的行业,将继续向前发展,我们将看到更多的趋势和技术变化。

现在轮到你翻盘了

吴恩达的 Python(异常检测)机器学习课程

原文:https://towardsdatascience.com/andrew-ngs-machine-learning-course-in-python-anomaly-detection-1233d23dba95?source=collection_archive---------1-----------------------

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

Machine Learning — Andrew Ng

T 这是吴恩达的机器学习课程 python 实现的最后一部分,我很高兴终于完成了这个系列。为了给你们一些视角,我花了一个月的时间将这些代码转换成 python,并为每个作业写了一篇文章。如果你们中的任何人正在犹豫要不要用 Python、R 或 Java 来实现,我强烈建议你们去做。从头开始编写这些算法不仅可以强化所教授的概念,还可以用自己熟悉的语言练习数据科学编程技能。

说了这么多,让我们进入最后一个编程作业

在这部分作业中,我们将使用高斯模型实现异常检测算法,首先检测 2D 数据集中的异常行为,然后检测高维数据集中的异常行为。

加载相关的库和数据集

import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmatmat = loadmat("ex8data1.mat")
X = mat["X"]
Xval = mat["Xval"]
yval = mat["yval"]

可视化数据

plt.scatter(X[:,0],X[:,1],marker="x")
plt.xlim(0,30)
plt.ylim(0,30)
plt.xlabel("Latency (ms)")
plt.ylabel("Throughput (mb/s)")

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

估计高斯模型的参数(均值和方差)

def estimateGaussian(X):
    """
     This function estimates the parameters of a Gaussian distribution using the data in X
    """

    m = X.shape[0]

    #compute mean
    sum_ = np.sum(X,axis=0)
    mu = 1/m *sum_

    # compute variance
    var = 1/m * np.sum((X - mu)**2,axis=0)

    return mu,varmu, sigma2 = estimateGaussian(X)

多元高斯分布是本课程的选修课,并给出了计算概率密度的代码。然而,为了让我继续这个任务,我需要从头开始编写multivariateGaussian 函数。

def multivariateGaussian(X, mu, sigma2):
    """
    Computes the probability density function of the multivariate gaussian distribution.
    """
    k = len(mu)

    sigma2=np.diag(sigma2)
    X = X - mu.T
    p = 1/((2*np.pi)**(k/2)*(np.linalg.det(sigma2)**0.5))* np.exp(-0.5* np.sum(X @ np.linalg.pinv(sigma2) * X,axis=1))
    return pp = multivariateGaussian(X, mu, sigma2)

我们在这里使用的一些有趣的函数来自 numpy 线性代数类。官方文件可以在这里找到。

一旦我们估计了高斯参数并获得了数据的概率密度,我们就可以可视化拟合。

plt.figure(figsize=(8,6))
plt.scatter(X[:,0],X[:,1],marker="x")
X1,X2 = np.meshgrid(np.linspace(0,35,num=70),np.linspace(0,35,num=70))
p2 = multivariateGaussian(np.hstack((X1.flatten()[:,np.newaxis],X2.flatten()[:,np.newaxis])), mu, sigma2)
contour_level = 10**np.array([np.arange(-20,0,3,dtype=np.float)]).T
plt.contour(X1,X2,p2[:,np.newaxis].reshape(X1.shape),contour_level)
plt.xlim(0,35)
plt.ylim(0,35)
plt.xlabel("Latency (ms)")
plt.ylabel("Throughput (mb/s)")

我之前没有解释过创建等高线图的过程,因为大多数都很简单。如果你理解起来有困难,这里的这篇文章可能会有所帮助。简单地说,我们首先在数据区域周围创建一个网格,并计算 Z 轴。plt.contour然后使用 3 个轴(X,Y,Z)创建等高线图。

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

现在选择一个阈值,将一个示例标记为异常。

def selectThreshold(yval, pval):
    """
    Find the best threshold (epsilon) to use for selecting outliers
    """
    best_epi = 0
    best_F1 = 0

    stepsize = (max(pval) -min(pval))/1000
    epi_range = np.arange(pval.min(),pval.max(),stepsize)
    for epi in epi_range:
        predictions = (pval<epi)[:,np.newaxis]
        tp = np.sum(predictions[yval==1]==1)
        fp = np.sum(predictions[yval==0]==1)
        fn = np.sum(predictions[yval==1]==0)

        # compute precision, recall and F1
        prec = tp/(tp+fp)
        rec = tp/(tp+fn)

        F1 = (2*prec*rec)/(prec+rec)

        if F1 > best_F1:
            best_F1 =F1
            best_epi = epi

    return best_epi, best_F1pval = multivariateGaussian(Xval, mu, sigma2)
epsilon, F1 = selectThreshold(yval, pval)
print("Best epsilon found using cross-validation:",epsilon)
print("Best F1 on Cross Validation Set:",F1)

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

如果您没有注意到,这里使用 F1 分数而不是准确性,因为数据集是高度不平衡的。为了了解更多关于评估机器学习模型性能的各种方法,这篇文章很好地总结了这个主题。

可视化最佳阈值

plt.figure(figsize=(8,6))# plot the data
plt.scatter(X[:,0],X[:,1],marker="x")# potting of contour
X1,X2 = np.meshgrid(np.linspace(0,35,num=70),np.linspace(0,35,num=70))
p2 = multivariateGaussian(np.hstack((X1.flatten()[:,np.newaxis],X2.flatten()[:,np.newaxis])), mu, sigma2)
contour_level = 10**np.array([np.arange(-20,0,3,dtype=np.float)]).T
plt.contour(X1,X2,p2[:,np.newaxis].reshape(X1.shape),contour_level)# Circling of anomalies
outliers = np.nonzero(p<epsilon)[0]
plt.scatter(X[outliers,0],X[outliers,1],marker ="o",facecolor="none",edgecolor="r",s=70)plt.xlim(0,35)
plt.ylim(0,35)
plt.xlabel("Latency (ms)")
plt.ylabel("Throughput (mb/s)")

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

对于高维数据集,我们只需遵循与之前完全相同的步骤

mat2 = loadmat("ex8data2.mat")
X2 = mat2["X"]
Xval2 = mat2["Xval"]
yval2 = mat2["yval"]# compute the mean and variance
mu2, sigma2_2 = estimateGaussian(X2)# Training set
p3 = multivariateGaussian(X2, mu2, sigma2_2)# cross-validation set
pval2 = multivariateGaussian(Xval2, mu2, sigma2_2)# Find the best threshold
epsilon2, F1_2 = selectThreshold(yval2, pval2)
print("Best epsilon found using cross-validation:",epsilon2)
print("Best F1 on Cross Validation Set:",F1_2)
print("# Outliers found:",np.sum(p3<epsilon2))

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

作业的第二部分包括实现一个协作过滤算法来建立一个电影分级推荐系统。

电影分级数据集的加载和可视化

mat3 = loadmat("ex8_movies.mat")
mat4 = loadmat("ex8_movieParams.mat")
Y = mat3["Y"] # 1682 X 943 matrix, containing ratings (1-5) of 1682 movies on 943 user
R = mat3["R"] # 1682 X 943 matrix, where R(i,j) = 1 if and only if user j give rating to movie i
X = mat4["X"] # 1682 X 10 matrix , num_movies X num_features matrix of movie features
Theta = mat4["Theta"] # 943 X 10 matrix, num_users X num_features matrix of user features# Compute average rating 
print("Average rating for movie 1 (Toy Story):",np.sum(Y[0,:]*R[0,:])/np.sum(R[0,:]),"/5")

打印语句将打印:Average rating for movie 1 (Toy Story): 3.8783185840707963 /5

plt.figure(figsize=(8,16))
plt.imshow(Y)
plt.xlabel("Users")
plt.ylabel("Movies")

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

进入算法本身,我们从计算成本函数和梯度开始

def  cofiCostFunc(params, Y, R, num_users, num_movies, num_features, Lambda):
    """
    Returns the cost and gradient for the collaborative filtering problem
    """

    # Unfold the params
    X = params[:num_movies*num_features].reshape(num_movies,num_features)
    Theta = params[num_movies*num_features:].reshape(num_users,num_features)

    predictions =  X @ Theta.T
    err = (predictions - Y)
    J = 1/2 * np.sum((err**2) * R)

    #compute regularized cost function
    reg_X =  Lambda/2 * np.sum(Theta**2)
    reg_Theta = Lambda/2 *np.sum(X**2)
    reg_J = J + reg_X + reg_Theta

    # Compute gradient
    X_grad = err*R @ Theta
    Theta_grad = (err*R).T @ X
    grad = np.append(X_grad.flatten(),Theta_grad.flatten())

    # Compute regularized gradient
    reg_X_grad = X_grad + Lambda*X
    reg_Theta_grad = Theta_grad + Lambda*Theta
    reg_grad = np.append(reg_X_grad.flatten(),reg_Theta_grad.flatten())

    return J, grad, reg_J, reg_grad

与前面的方法类似,该任务要求我们在单独的步骤中计算成本函数、梯度、正则化成本函数,然后正则化梯度。只要你使用正确的索引,上面的代码块将允许你一步一步地跟随任务。

为了测试我们的成本函数,

# Reduce the data set size to run faster
num_users, num_movies, num_features = 4,5,3
X_test = X[:num_movies,:num_features]
Theta_test= Theta[:num_users,:num_features]
Y_test = Y[:num_movies,:num_users]
R_test = R[:num_movies,:num_users]
params = np.append(X_test.flatten(),Theta_test.flatten())# Evaluate cost function
J, grad = cofiCostFunc(params, Y_test, R_test, num_users, num_movies, num_features, 0)[:2]
print("Cost at loaded parameters:",J)J2, grad2 = cofiCostFunc(params, Y_test, R_test, num_users, num_movies, num_features, 1.5)[2:]
print("Cost at loaded parameters (lambda = 1.5):",J2)

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

一旦我们得到了我们的成本函数和梯度,我们可以开始训练我们的算法。

加载电影列表

# load movie list
movieList = open("movie_ids.txt","r").read().split("\n")[:-1]# see movie list
np.set_printoptions(threshold=np.nan)
movieList

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

你可以在这一步输入你自己的电影偏好,但我使用了与作业完全相同的评分来保持一致。

# Initialize my ratings
my_ratings = np.zeros((1682,1))# Create own ratings
my_ratings[0] = 4 
my_ratings[97] = 2
my_ratings[6] = 3
my_ratings[11]= 5
my_ratings[53] = 4
my_ratings[63]= 5
my_ratings[65]= 3
my_ratings[68] = 5
my_ratings[82]= 4
my_ratings[225] = 5
my_ratings[354]= 5print("New user ratings:\n")
for i in range(len(my_ratings)):
    if my_ratings[i]>0:
        print("Rated",int(my_ratings[i]),"for index",movieList[i])

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

为了在输入到算法中之前准备我们的数据,我们需要标准化评级,设置一些随机的初始参数,并使用优化算法来更新参数。

def normalizeRatings(Y, R):
    """
    normalized Y so that each movie has a rating of 0 on average, and returns the mean rating in Ymean.
    """

    m,n = Y.shape[0], Y.shape[1]
    Ymean = np.zeros((m,1))
    Ynorm = np.zeros((m,n))

    for i in range(m):
        Ymean[i] = np.sum(Y[i,:])/np.count_nonzero(R[i,:])
        Ynorm[i,R[i,:]==1] = Y[i,R[i,:]==1] - Ymean[i]

    return Ynorm, Ymeandef gradientDescent(initial_parameters,Y,R,num_users,num_movies,num_features,alpha,num_iters,Lambda):
    """
    Optimize X and Theta
    """
    # unfold the parameters
    X = initial_parameters[:num_movies*num_features].reshape(num_movies,num_features)
    Theta = initial_parameters[num_movies*num_features:].reshape(num_users,num_features)

    J_history =[]

    for i in range(num_iters):
        params = np.append(X.flatten(),Theta.flatten())
        cost, grad = cofiCostFunc(params, Y, R, num_users, num_movies, num_features, Lambda)[2:]

        # unfold grad
        X_grad = grad[:num_movies*num_features].reshape(num_movies,num_features)
        Theta_grad = grad[num_movies*num_features:].reshape(num_users,num_features)
        X = X - (alpha * X_grad)
        Theta = Theta - (alpha * Theta_grad)
        J_history.append(cost)

    paramsFinal = np.append(X.flatten(),Theta.flatten())
    return paramsFinal , J_history

再次,我选择批量梯度下降作为我的优化算法。在 python 中完成所有编程任务教会我的一件事是,梯度下降很少出错。在这一点上,梯度下降的代码应该是相当熟悉的。

Y = np.hstack((my_ratings,Y))
R =np.hstack((my_ratings!=0,R))# Normalize Ratings
Ynorm, Ymean = normalizeRatings(Y, R)num_users = Y.shape[1]
num_movies = Y.shape[0]
num_features = 10# Set initial Parameters (Theta,X)
X = np.random.randn(num_movies, num_features)
Theta = np.random.randn(num_users, num_features)
initial_parameters = np.append(X.flatten(),Theta.flatten())
Lambda = 10# Optimize parameters using Gradient Descent
paramsFinal, J_history = gradientDescent(initial_parameters,Y,R,num_users,num_movies,num_features,0.001,400,Lambda)

绘制成本函数以确保梯度下降有效

plt.plot(J_history)
plt.xlabel("Iteration")
plt.ylabel("$J(\Theta)$")
plt.title("Cost function using Gradient Descent")

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

对你没有评级的电影进行预测

# unfold paramaters
X = paramsFinal[:num_movies*num_features].reshape(num_movies,num_features)
Theta = paramsFinal[num_movies*num_features:].reshape(num_users,num_features)# Predict rating
p = X @ Theta.T
my_predictions = p[:,0][:,np.newaxis] + Ymeanimport pandas as pd
df = pd.DataFrame(np.hstack((my_predictions,np.array(movieList)[:,np.newaxis])))
df.sort_values(by=[0],ascending=False,inplace=True)
df.reset_index(drop=True,inplace=True)print("Top recommendations for you:\n")
for i in range(10):
    print("Predicting rating",round(float(df[0][i]),1)," for index",df[1][i])

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

最后,我结束了吴恩达的 Python 机器学习课程。我希望这对你和我写这篇文章一样有益,我感谢你们所有人的支持。

Jupyter 笔记本会上传到我的 GitHub 上(https://GitHub . com/Ben lau 93/Machine-Learning-by-Andrew-Ng-in-Python)。

对于本系列中的其他 python 实现,

感谢您的阅读。

吴恩达的 Python (Kmeans-Clustering,PCA)机器学习课程

原文:https://towardsdatascience.com/andrew-ngs-machine-learning-course-in-python-kmeans-clustering-pca-b7ba6fafa74?source=collection_archive---------12-----------------------

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

Machine Learning — Andrew Ng

本系列的倒数第二部分,我们来看看由无标签数据组成的无监督学习算法。让我们直接进入作业,因为我们今天要学习两个算法。

K-意思是聚类是一种聚类分析技术,允许将数据分组到称为聚类的组中。因为没有为每个训练数据提供标签,所以通过数据彼此的相似性来确定聚类。

我们将从实现 K-means 算法开始。由于 K-means 是一个迭代过程,它将训练样本分配给它们最近的质心,然后重新计算质心,我们需要两个主要函数来完成这一任务。

import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmatmat = loadmat("ex7data2.mat")
X = mat["X"]

findClosestCentroids通过评估训练样本与每个质心之间的距离来找到最近的质心,并将质心分配给具有最小距离的训练样本。

def findClosestCentroids(X, centroids):
    """
    Returns the closest centroids in idx for a dataset X where each row is a single example.
    """
    K = centroids.shape[0]
    idx = np.zeros((X.shape[0],1))
    temp = np.zeros((centroids.shape[0],1))

    for i in range(X.shape[0]):
        for j in range(K):
            dist = X[i,:] - centroids[j,:]
            length = np.sum(dist**2)
            temp[j] = length
        idx[i] = np.argmin(temp)+1
    return idx# Select an initial set of centroids
K = 3
initial_centroids = np.array([[3,3],[6,2],[8,5]])
idx = findClosestCentroids(X, initial_centroids)
print("Closest centroids for the first 3 examples:\n",idx[0:3])

np.argmin找出距离最小的指标,赋给训练样本。这里使用+1 从 1 而不是 0 开始对质心进行编号。

print 语句将打印:

前三个例子的最近质心:
[[1。】
【3。】
【2。]]

为了计算分配后的质心平均值,我们将分配给特定质心的训练样本相加,然后除以每个质心中的样本数。

def computeCentroids(X, idx, K):
    """
    returns the new centroids by computing the means of the data points assigned to each centroid.
    """
    m, n = X.shape[0],X.shape[1]
    centroids = np.zeros((K,n))
    count = np.zeros((K,1))

    for i in range(m):
        index = int((idx[i]-1)[0])
        centroids[index,:]+=X[i,:]
        count[index]+=1

    return centroids/countcentroids = computeCentroids(X, idx, K)
print("Centroids computed after initial finding of closest centroids:\n", centroids)

print 语句将打印:

在最初找到最近的质心之后计算的质心:
[[2.42830111 3.15792418]
[5.81350331 2.63365645]
[7.11938687 3.6166844]]

现在,为了可视化整个过程,我为算法的每次迭代创建了一个子图,以监控质心的移动和训练示例的分配。

def plotKmeans(X, centroids, idx, K, num_iters):
    """
    plots the data points with colors assigned to each centroid
    """
    m,n = X.shape[0],X.shape[1]

    fig, ax = plt.subplots(nrows=num_iters,ncols=1,figsize=(6,36))

    for i in range(num_iters):    
        # Visualisation of data
        color = "rgb"
        for k in range(1,K+1):
            grp = (idx==k).reshape(m,1)
            ax[i].scatter(X[grp[:,0],0],X[grp[:,0],1],c=color[k-1],s=15)# visualize the new centroids
        ax[i].scatter(centroids[:,0],centroids[:,1],s=120,marker="x",c="black",linewidth=3)
        title = "Iteration Number " + str(i)
        ax[i].set_title(title)

        # Compute the centroids mean
        centroids = computeCentroids(X, idx, K)

        # assign each training example to the nearest centroid
        idx = findClosestCentroids(X, centroids)

    plt.tight_layout()m,n = X.shape[0],X.shape[1]
plotKmeans(X, initial_centroids,idx, K,10)

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

因为 K-means 算法并不总是给出最优解,所以随机初始化很重要。

def kMeansInitCentroids(X, K):
    """
    This function initializes K centroids that are to beused in K-Means on the dataset X
    """
    m,n = X.shape[0], X.shape[1]
    centroids = np.zeros((K,n))

    for i in range(K):
        centroids[i] = X[np.random.randint(0,m+1),:]

    return centroidscentroids = kMeansInitCentroids(X, K)
idx = findClosestCentroids(X, centroids)
plotKmeans(X, centroids,idx, K,10)

上面的代码将再次运行可视化,但会进行随机初始化。您可以多次运行代码来查看随机初始质心的影响。

现在我们完成了算法的编码,我们可以开始用其他数据集实现它了。在本练习中,我们将使用该算法选择 16 个聚类来代表图像(从数千种颜色中选择)以压缩图像。

mat2 = loadmat("bird_small.mat")
A = mat2["A"]# preprocess and reshape the image
X2 = (A/255).reshape(128*128,3)def runKmeans(X, initial_centroids,num_iters,K):

    idx = findClosestCentroids(X, initial_centroids)

    for i in range(num_iters):

        # Compute the centroids mean
        centroids = computeCentroids(X, idx, K)# assign each training example to the nearest centroid
        idx = findClosestCentroids(X, initial_centroids)return centroids, idx

现在对数据集运行 k-means 算法

K2 = 16
num_iters = 10
initial_centroids2 = kMeansInitCentroids(X2, K2)
centroids2, idx2 = runKmeans(X2, initial_centroids2, num_iters,K2)m2,n2 = X.shape[0],X.shape[1]
X2_recovered = X2.copy()
for i in range(1,K2+1):
    X2_recovered[(idx2==i).ravel(),:] = centroids2[i-1]# Reshape the recovered image into proper dimensions
X2_recovered = X2_recovered.reshape(128,128,3)# Display the image
import matplotlib.image as mpimg
fig, ax = plt.subplots(1,2)
ax[0].imshow(X2.reshape(128,128,3))
ax[1].imshow(X2_recovered)

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

这是原始图像和只有 16 种颜色的压缩图像的并排比较。

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

Machine Learning — Andrew Ng

作业的下一部分利用 2D 数据集来获得对主成分分析(PCA)过程的直觉,然后在人脸图像数据集上进行 PCA 以执行降维。

加载并可视化 2D 数据集

mat3 = loadmat("ex7data1.mat")
X3 = mat3["X"]plt.scatter(X3[:,0],X3[:,1],marker="o",facecolors="none",edgecolors="b")

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

为了实现 PCA 算法,PCA 还包括两个计算步骤,我们将对其中一个步骤进行编码以计算协方差矩阵,并对另一个步骤使用 numpy 库以获得特征向量。

在此之前,需要进行特征归一化,以确保数据在同一范围内。

def featureNormalize(X):
    """
    Returns a normalized version of X where the mean value of each feature is 0 and the standard deviation is 1.
    """
    mu = np.mean(X,axis=0)
    sigma = np.std(X,axis=0)

    X_norm = (X - mu)/sigma

    return X_norm, mu , sigmadef pca(X):
    """
    Computes eigenvectors of the covariance matrix of X
    """
    m,n = X.shape[0], X.shape[1]

    sigma = 1/m * X.T @ X

    U,S,V = svd(sigma)

    return U,S,V

np.linalg.svd类似于 matlab 中的svd函数,返回相同的 U、S、V 矩阵。官方文档可以在这里找到。

from numpy.linalg import svd
X_norm,mu,std = featureNormalize(X3)
U,S = pca(X_norm)[:2]plt.scatter(X3[:,0],X3[:,1],marker="o",facecolors="none",edgecolors="b")
plt.plot([mu[0],(mu+1.5*S[0]*U[:,0].T)[0]],[mu[1],(mu+1.5*S[0]*U[:,0].T)[1]],color="black",linewidth=3)
plt.plot([mu[0],(mu+1.5*S[1]*U[:,1].T)[0]],[mu[1],(mu+1.5*S[1]*U[:,1].T)[1]],color="black",linewidth=3)
plt.xlim(-1,7)
plt.ylim(2,8)

上面的代码块在数据集上实现 PCA,并可视化数据上的特征向量。我发现维基百科为大多数学习算法提供了很好的信息来源,如果你想更深入地研究算法,绝对值得一看。

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

print("Top eigenvector U(:,1) =:",U[:,0])`

打印报表打印:Top eigenvector U(:,1) =: [-0.70710678 -0.70710678]

为了减少数据集的维度,我们将数据投影到找到的主成分(特征向量)上。

def projectData(X, U, K):
    """
    Computes the reduced data representation when projecting only on to the top k eigenvectors
    """
    m = X.shape[0]
    U_reduced = U[:,:K]
    Z = np.zeros((m,K))

    for i in range(m):
        for j in range(K):
            Z[i,j] = X[i,:] @ U_reduced[:,j]

    return Z# Project the data onto K=1 dimension
K=1
Z = projectData(X_norm, U, K)
print("Projection of the first example:",Z[0][0])

打印语句将打印:Projection of the first example: 1.4963126084578515

也可以通过将数据投影回原始维度空间来近似重构数据。

def recoverData(Z, U, K):
    """
    Recovers an approximation of the original data when using the projected data
    """
    m,n = Z.shape[0],U.shape[0]
    X_rec = np.zeros((m,n))
    U_reduced = U[:,:K]

    for i in range(m):
        X_rec[i,:] = Z[i,:] @ U_reduced.T

    return X_recX_rec  = recoverData(Z, U, K)
print("Approximation of the first example:",X_rec[0,:])

打印语句将打印:Approximation of the first example: [-1.05805279 -1.05805279]

为了可视化整个过程,

plt.scatter(X_norm[:,0],X_norm[:,1],marker="o",label="Original",facecolors="none",edgecolors="b",s=15)
plt.scatter(X_rec[:,0],X_rec[:,1],marker="o",label="Approximation",facecolors="none",edgecolors="r",s=15)
plt.title("The Normalized and Projected Data after PCA")
plt.legend()

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

最后,我们转向更复杂的数据集——人脸图像数据集。为了加载和可视化数据,

mat4 = loadmat("ex7faces.mat")
X4 = mat4["X"]fig, ax = plt.subplots(nrows=10,ncols=10,figsize=(8,8))
for i in range(0,100,10):
    for j in range(10):
        ax[int(i/10),j].imshow(X4[i+j,:].reshape(32,32,order="F"),cmap="gray")
        ax[int(i/10),j].axis("off")

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

这些图像包含 32 X 32 像素的灰度,导致 1,024 个特征的维度,我们的任务是将维度减少到大约 100 个最能描述我们数据的主成分。

X_norm2 = featureNormalize(X4)[0]# Run PCA
U2 =pca(X_norm2)[0]#Visualize the top 36 eigenvectors found
U_reduced = U2[:,:36].T
fig2, ax2 = plt.subplots(6,6,figsize=(8,8))
for i in range(0,36,6):
    for j in range(6):
        ax2[int(i/6),j].imshow(U_reduced[i+j,:].reshape(32,32,order="F"),cmap="gray")
        ax2[int(i/6),j].axis("off")

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

上面是描述数据集中最大变化的 36 个主成分的可视化。

接下来,我们将数据投射到前 100 个主成分上,有效地将维度降低到 100,恢复数据并尝试理解在维度降低过程中丢失了什么。

K2 = 100
Z2 = projectData(X_norm2, U2, K2)
print("The projected data Z has a size of:",Z2.shape)# Data reconstruction
X_rec2  = recoverData(Z2, U2, K2)# Visualize the reconstructed data
fig3, ax3 = plt.subplots(10,10,figsize=(8,8))
for i in range(0,100,10):
    for j in range(10):
        ax3[int(i/10),j].imshow(X_rec2[i+j,:].reshape(32,32,order="F"),cmap="gray")
        ax3[int(i/10),j].axis("off")

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

这是无监督学习的终结。请期待该系列的最后一篇文章。Jupyter 笔记本会上传到我的 GitHub 上(https://GitHub . com/Ben lau 93/Machine-Learning-by-Andrew-Ng-in-Python)。

对于本系列中的其他 python 实现,

感谢您的阅读。

吴恩达的 Python(支持向量机)机器学习教程

原文:https://towardsdatascience.com/andrew-ngs-machine-learning-course-in-python-support-vector-machines-435fc34b7bf9?source=collection_archive---------11-----------------------

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

Machine Learning — Andrew Ng

继续这个系列,我们将继续学习编程作业 6 的支持向量机。如果你注意到了,我没有写作业 5,因为大多数任务只需要绘制和解释学习曲线。不过你还是可以在我的 GitHub 里找到代码,网址是https://GitHub . com/Ben lau 93/Machine-Learning-by-Andrew-Ng-in-Python/tree/master/Bias _ Vs _ Variance

T4:这个作业有两个部分。首先,我们将在几个 2D 数据集上实现支持向量机(SVM ),以直观地了解算法及其工作原理。接下来,我们将在电子邮件数据集上使用 SVM 来尝试对垃圾邮件进行分类。

为了加载数据集,使用来自 scipy.io 的 loadmat 来打开 mat 文件

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmatmat = loadmat("ex6data1.mat")
X = mat["X"]
y = mat["y"]

数据集的绘图

m,n = X.shape[0],X.shape[1]
pos,neg= (y==1).reshape(m,1), (y==0).reshape(m,1)
plt.scatter(X[pos[:,0],0],X[pos[:,0],1],c="r",marker="+",s=50)
plt.scatter(X[neg[:,0],0],X[neg[:,0],1],c="y",marker="o",s=50)

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

我们从一个简单的数据集开始,它在训练示例之间有一个清晰的线性边界。

正如讲座中所建议的,我们尽量不从头开始编写 SVM,而是利用高度优化的库,如 sklearn 来完成这项任务。官方文档可以在这里找到。

from sklearn.svm import SVC
classifier = SVC(kernel="linear")
classifier.fit(X,np.ravel(y))

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

由于这是一个线性分类问题,我们将不会为这个任务使用任何内核。这相当于在 SVC 中使用线性核(注意 SVC 的默认核设置是“rbf”,代表径向基函数)。这里的ravel()函数返回一个大小为(m)的数组,这是 SVC 所需要的。

plt.figure(figsize=(8,6))
plt.scatter(X[pos[:,0],0],X[pos[:,0],1],c="r",marker="+",s=50)
plt.scatter(X[neg[:,0],0],X[neg[:,0],1],c="y",marker="o",s=50)# plotting the decision boundary
X_1,X_2 = np.meshgrid(np.linspace(X[:,0].min(),X[:,1].max(),num=100),np.linspace(X[:,1].min(),X[:,1].max(),num=100))
plt.contour(X_1,X_2,classifier.predict(np.array([X_1.ravel(),X_2.ravel()]).T).reshape(X_1.shape),1,colors="b")
plt.xlim(0,4.5)
plt.ylim(1.5,5)

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

C=1, kernel = “linear”

在默认设置为 C = 1.0(记住 C = 1/λ)的情况下,这就是我们得到的决策边界。

# Test C = 100
classifier2 = SVC(C=100,kernel="linear")
classifier2.fit(X,np.ravel(y))

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

plt.figure(figsize=(8,6))
plt.scatter(X[pos[:,0],0],X[pos[:,0],1],c="r",marker="+",s=50)
plt.scatter(X[neg[:,0],0],X[neg[:,0],1],c="y",marker="o",s=50)# plotting the decision boundary
X_3,X_4 = np.meshgrid(np.linspace(X[:,0].min(),X[:,1].max(),num=100),np.linspace(X[:,1].min(),X[:,1].max(),num=100))
plt.contour(X_3,X_4,classifier2.predict(np.array([X_3.ravel(),X_4.ravel()]).T).reshape(X_3.shape),1,colors="b")
plt.xlim(0,4.5)
plt.ylim(1.5,5)

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

C= 100, kernel =”linear”

更改 C=100,给出一个超过训练示例的决策边界。

接下来,我们将看一个不能线性分离的数据集。这就是内核发挥作用的地方,为我们提供非线性分类器的功能。对于那些理解内核概念有困难的人来说,我找到的这篇文章给出了一个很好的关于内核的直觉和一些数学解释。对于这部分作业,我们需要完成函数gaussianKernel来帮助实现高斯核的 SVM。我将跳过这一步,因为 SVC 包含它自己的径向基函数(rbf)形式的高斯核实现。这里的是维基百科的页面,上面有 rbf 的方程,正如你看到的,它与课程中的高斯核函数相同。

示例数据集 2 的加载和绘图

mat2 = loadmat("ex6data2.mat")
X2 = mat2["X"]
y2 = mat2["y"]m2,n2 = X2.shape[0],X2.shape[1]
pos2,neg2= (y2==1).reshape(m2,1), (y2==0).reshape(m2,1)
plt.figure(figsize=(8,6))
plt.scatter(X2[pos2[:,0],0],X2[pos2[:,0],1],c="r",marker="+")
plt.scatter(X2[neg2[:,0],0],X2[neg2[:,0],1],c="y",marker="o")
plt.xlim(0,1)
plt.ylim(0.4,1)

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

用高斯核实现 SVM

classifier3 = SVC(kernel="rbf",gamma=30)
classifier3.fit(X2,y2.ravel())

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

对于径向基函数核的 SVM 的参数,它使用 gamma 代替 sigma。参数文档可以在这里找到。我发现 gamma 类似于 1/σ,但不完全是,我希望一些领域专家能给我解释这个 gamma 项的见解。至于这个数据集,我发现 gamma 值为 30 表示与作业中的优化参数最相似(课程中 sigma 为 0.1)。

plt.figure(figsize=(8,6))
plt.scatter(X2[pos2[:,0],0],X2[pos2[:,0],1],c="r",marker="+")
plt.scatter(X2[neg2[:,0],0],X2[neg2[:,0],1],c="y",marker="o")# plotting the decision boundary
X_5,X_6 = np.meshgrid(np.linspace(X2[:,0].min(),X2[:,1].max(),num=100),np.linspace(X2[:,1].min(),X2[:,1].max(),num=100))
plt.contour(X_5,X_6,classifier3.predict(np.array([X_5.ravel(),X_6.ravel()]).T).reshape(X_5.shape),1,colors="b")
plt.xlim(0,1)
plt.ylim(0.4,1)

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

C = 1, gamma = 30, kernel = “rbf”

对于本部分的最后一个数据集,我们执行一个简单的超参数调整,以确定要使用的最佳 C 值和 gamma 值。

示例数据集 3 的加载和绘图

mat3 = loadmat("ex6data3.mat")
X3 = mat3["X"]
y3 = mat3["y"]
Xval = mat3["Xval"]
yval = mat3["yval"]m3,n3 = X3.shape[0],X3.shape[1]
pos3,neg3= (y3==1).reshape(m3,1), (y3==0).reshape(m3,1)
plt.figure(figsize=(8,6))
plt.scatter(X3[pos3[:,0],0],X3[pos3[:,0],1],c="r",marker="+",s=50)
plt.scatter(X3[neg3[:,0],0],X3[neg3[:,0],1],c="y",marker="o",s=50)

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

def dataset3Params(X, y, Xval, yval,vals):
    """
    Returns your choice of C and sigma. You should complete this function to return the optimal C and 
    sigma based on a cross-validation set.
    """
    acc = 0
    best_c=0
    best_gamma=0
    for i in vals:
        C= i
        for j in vals:
            gamma = 1/j
            classifier = SVC(C=C,gamma=gamma)
            classifier.fit(X,y)
            prediction = classifier.predict(Xval)
            score = classifier.score(Xval,yval)
            if score>acc:
                acc =score
                best_c =C
                best_gamma=gamma
    return best_c, best_gamma

dataset3Params遍历函数中给出的vals列表,将 C 设置为 val,将 gamma 设置为 1/val。使用每个参数组合构建 SVC 模型,并计算验证集的准确度。基于精度,选择最佳模型,并返回相应的 C 和 gamma 值。

vals = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30]
C, gamma = dataset3Params(X3, y3.ravel(), Xval, yval.ravel(),vals)
classifier4 = SVC(C=C,gamma=gamma)
classifier4.fit(X3,y3.ravel())

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

plt.figure(figsize=(8,6))
plt.scatter(X3[pos3[:,0],0],X3[pos3[:,0],1],c="r",marker="+",s=50)
plt.scatter(X3[neg3[:,0],0],X3[neg3[:,0],1],c="y",marker="o",s=50)# plotting the decision boundary
X_7,X_8 = np.meshgrid(np.linspace(X3[:,0].min(),X3[:,1].max(),num=100),np.linspace(X3[:,1].min(),X3[:,1].max(),num=100))
plt.contour(X_7,X_8,classifier4.predict(np.array([X_7.ravel(),X_8.ravel()]).T).reshape(X_7.shape),1,colors="b")
plt.xlim(-0.6,0.3)
plt.ylim(-0.7,0.5)

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

C = 0.3, gamma = 100, kernel =”rbf”

C 的最佳值是 0.3,γ的最佳值是 100,这导致了与分配相似的决策边界。

继续垃圾邮件分类。这个问题是独特的,因为它更侧重于数据预处理,而不是实际的建模过程。电子邮件需要以一种可以用作模型输入的方式进行处理。一种方法是根据常用词汇列表获取电子邮件中所有单词的索引。

加载数据

import re
from nltk.stem import PorterStemmerfile_contents = open("emailSample1.txt","r").read()
vocabList = open("vocab.txt","r").read()

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

Content of the email

给出了词汇列表及其相应的索引,我将该列表存储为字典,以词汇作为键,索引作为值。你也许可以用另一种方式来做,但是我想让访问词汇更容易(比如用 if keys in dict)

vocabList=vocabList.split("\n")[:-1]vocabList_d={}
for ea in vocabList:
    value,key = ea.split("\t")[:]
    vocabList_d[key] = value

至于邮件的预处理,作业中为我们概述了几个步骤。

def processEmail(email_contents,vocabList_d):
    """
    Preprocesses the body of an email and returns a list of indices of the words contained in the email. 
    """
    # Lower case
    email_contents = email_contents.lower()

    # Handle numbers
    email_contents = re.sub("[0-9]+","number",email_contents)

    # Handle URLS
    email_contents = re.sub("[http|https]://[^\s]*","httpaddr",email_contents)

    # Handle Email Addresses
    email_contents = re.sub("[^\s]+@[^\s]+","emailaddr",email_contents)

    # Handle $ sign
    email_contents = re.sub("[$]+","dollar",email_contents)

    # Strip all special characters
    specialChar = ["<","[","^",">","+","?","!","'",".",",",":"]
    for char in specialChar:
        email_contents = email_contents.replace(str(char),"")
    email_contents = email_contents.replace("\n"," ")    

    # Stem the word
    ps = PorterStemmer()
    email_contents = [ps.stem(token) for token in email_contents.split(" ")]
    email_contents= " ".join(email_contents)

    # Process the email and return word_indices

    word_indices=[]

    for char in email_contents.split():
        if len(char) >1 and char in vocabList_d:
            word_indices.append(int(vocabList_d[char]))

    return word_indicesword_indices= processEmail(file_contents,vocabList_d)

正则表达式的使用在这里非常方便,python 大师的这个教程可以帮助你开始 re。这里另一个有用的库是 nlkt,其中的PorterStemmer() 函数有助于词干化。另一个好的教程是 pythonprogramming.net 的。

在得到单词的索引后,我们需要将索引转换成一个特征向量。

def emailFeatures(word_indices, vocabList_d):
    """
    Takes in a word_indices vector and  produces a feature vector from the word indices. 
    """
    n = len(vocabList_d)

    features = np.zeros((n,1))

    for i in word_indices:
        features[i] =1

    return featuresfeatures = emailFeatures(word_indices,vocabList_d)
print("Length of feature vector: ",len(features))
print("Number of non-zero entries: ",np.sum(features))

打印语句将打印:Length of feature vector: 1899Number of non-zero entries: 43.0。这与赋值略有不同,因为在赋值中,you’re被捕获为“你”和“re ”,而我的代码将它识别为“你的”,导致更少的非零条目。

训练 SVM 就像将特征作为输入传递一样简单。然而,这只是一个训练示例,我们需要更多的训练数据来训练分类器。

spam_mat = loadmat("spamTrain.mat")
X_train =spam_mat["X"]
y_train = spam_mat["y"]

训练示例在spamTrain.mat中给出,用于训练我们的分类器,而测试示例在spamTest.mat中给出,用于确定我们的模型可推广性。

C =0.1
spam_svc = SVC(C=0.1,kernel ="linear")
spam_svc.fit(X_train,y_train.ravel())
print("Training Accuracy:",(spam_svc.score(X_train,y_train.ravel()))*100,"%")

打印语句将打印:Training Accuracy: 99.825 %

spam_mat_test = loadmat("spamTest.mat")
X_test = spam_mat_test["Xtest"]
y_test =spam_mat_test["ytest"]spam_svc.predict(X_test)
print("Test Accuracy:",(spam_svc.score(X_test,y_test.ravel()))*100,"%")

打印语句将打印: Test Accuracy: 98.9 %

为了更好地理解我们的模型,我们可以查看每个单词的权重,并找出最能预测垃圾邮件的单词。

weights = spam_svc.coef_[0]
weights_col = np.hstack((np.arange(1,1900).reshape(1899,1),weights.reshape(1899,1)))
df = pd.DataFrame(weights_col)df.sort_values(by=[1],ascending = False,inplace=True)predictors = []
idx=[]
for i in df[0][:15]:
    for keys, values in vocabList_d.items():
        if str(int(i)) == values:
            predictors.append(keys)
            idx.append(int(values))print("Top predictors of spam:")for _ in range(15):
    print(predictors[_],"\t\t",round(df[1][idx[_]-1],6))

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

支持向量机就是这样!jupyter 笔记本会上传到我的 GitHub 上(https://GitHub . com/Ben lau 93/Machine-Learning-by-Andrew-Ng-in-Python)。

对于本系列中的其他 python 实现,

感谢您的阅读。

安卓智能手机使用分析,以减少手机成瘾

原文:https://towardsdatascience.com/android-smartphone-usage-analysis-in-r-to-reduce-phone-addiction-135bd0084e41?source=collection_archive---------16-----------------------

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

使用您的数据科学技能来改善您的日常生活会有多有趣?因此,我决定利用谷歌让我们下载的数据,开始挖掘我的 Android 手机应用程序的使用情况。我张贴这篇文章的原因是为了让其他人反省他们的用法并了解它。因此,如果有人想复制我的结果,我将在下面的步骤中解释如何下载数据。

如何下载您的 Android 手机使用数据:

  • 进入你的谷歌账户(使用你在安卓手机上用过的 Gmail id)——如果你已经登录,跳过这一步。
  • 谷歌外卖
  • 点击选择无按钮,向下滚动查看我的活动
  • 选择我的活动(启用灰色/蓝色按钮)并单击向下箭头选择 JSON 格式,然后单击下一步(最底部的按钮)
  • 在下一个屏幕中,您可以选择您喜欢的下载方法和文件格式,然后单击 Create Archive。

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

一旦你的数据准备好下载,你会被通知下载。下载的文件将是一个压缩文件(最像 Zip —基于您在最后一个屏幕中的选择)。因此,解压缩它并保留 JSON 文件,以便我们继续进行下一步。

开始分析

使用的包

我们将使用下面的包进行分析。

library(jsonlite)
library(tidyverse)
library(lubridate)
library(ggrepel)
library(viridis)
library(gganimate)
library(cowplot)
library(ggthemes)

如果你还没有得到任何上述软件包,所有这些都可以在克兰。所以,使用install.packages()来安装缺失的包。

加载数据

我们有一个 JSON 输入文件,最好用 Dataframe 进行分析(因为它很适合 tidyverse)。但是这种数据处理在jsonlite的 omJSON()的帮助下简直易如反掌

接受 JSON 文件并输出扁平数据帧的函数。

me <- jsonlite::fromJSON("MyActivity.json")

有了上面的代码,我们就可以开始数据预处理了。

数据预处理

我们在分析中会用到的一列,时间

以字符串形式出现,其中包含数据和时间。但是对于我们来说,将时间作为时间处理—它必须是日期-时间格式,所以我们将使用函数 parse_date_time()将字符串转换为日期-时间,并使用 withtz()

更改时区。因为我住在印度,所以我把它转换成了印度标准时间。请使用您合适的时区进行转换。

# converting date-time in string to date-time format along with time-zone conversion
me$time_ist <- with_tz(parse_date_time(me$time),"Asia/Calcutta") 
# remove incomplete years and irrelevant years too - Kept 2019 to see just January if required
me <- filter(me, year(time_ist) %in% c(2017,2018,2019))

正如你在上面的代码中看到的,我们还过滤了我们的数据,只包括 2017 年、2018 年和 2019 年。这只是为了避免部分数据。尽管 2019 年也是部分数据,但我决定将其包含在主要数据中,以比较我在这三年中的应用。至此,我们已经做好了数据预处理,让我们开始分析。

数据注释

这里必须指出的一点是,这种活动数据包括您打开的应用程序和通知中显示的应用程序的所有内容,因此我们进一步假设每个通知也是我们交互的一部分(或者至少在我的情况下,每次弹出通知时,我都会检查它)。

样本/头部数据

# Sample
tibble::tibble(head(me))
*# A tibble: 6 x 1
  `head(me)`$header  $title   $titleUrl     $time  $products $details $time_ist          

1 OnePlus Launcher   Used On… https://play… 2019-…     2019-02-12 12:34:01
2 صلاتك Salatuk (Pr… Used صل… https://play… 2019-…     2019-02-12 12:34:01
3 Google Chrome: Fa… Used Go… https://play… 2019-…     2019-02-12 12:19:23
4 Firefox Browser f… Used Fi… https://play… 2019-…     2019-02-12 12:18:38
5 Hangouts           Used Ha… https://play… 2019-…     2019-02-12 12:18:15
6 Gmail              Used Gm… https://play… 2019-…     2019-02-12 12:17:50*

热门应用—每年

本节中的代码将绘制三个不同年份的三个不同的热门应用程序使用图,并最终将它们缝合在一起。

# Top apps
me_count <- me %>% 
  group_by(year = year(time_ist),header) %>% 
  count() %>% 
  arrange(desc(n)) %>% 
  ungroup() %>% 
  group_by(year) %>% 
  top_n(20,n) #%>% #View() 
#mutate(header = fct_reorder(header,n)) %>% me_count %>%  
  filter(year %in% "2017") %>% 
  ggplot(aes(fct_reorder(header,n),n, label = n)) +     
  geom_bar(aes(fill = n),stat = "identity") +
  #scale_y_log10() +
  coord_flip() +
  theme(axis.text.y = element_text(angle = 0, hjust = 1,size = 8))  +
  scale_fill_viridis() +
  theme_minimal() +
  theme(legend.position="none") +
  labs(
    title = "Most used 20 Apps",
    subtitle = "2017",
    x = "App name"
  ) -> y1me_count %>%  
  filter(year %in% "2018") %>% 
  ggplot(aes(fct_reorder(header,n),n, label = n)) +  
  geom_bar(aes(fill = n),stat = "identity") +
  #scale_y_log10() +
  coord_flip() +
  theme(axis.text.y = element_text(angle = 0, hjust = 1,size = 8))  +
  scale_fill_viridis() +
    theme_minimal() +
  theme(legend.position="none") +
  labs(
    subtitle = "2018",
    x = "App name"
  ) -> y2me_count %>%  
  filter(year %in% "2019") %>% 
  ggplot(aes(fct_reorder(header,n),n, label = n)) +  
  geom_bar(aes(fill = n),stat = "identity") +
  #scale_y_log10() +
  coord_flip() +
  theme(axis.text.y = element_text(angle = 0, hjust = 1,size = 8))  +
  scale_fill_viridis() +
  theme_minimal() +
  theme(legend.position="none") +
  labs(
    subtitle = "2019",
    x = "App name"
  ) -> y3
cowplot::plot_grid(y1,y2,y3, ncol = 3, scale = 0.7, vjust = 0, label_size = 8)

给出了这个图:

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

这张图清楚地告诉我,随着时间的推移,我的应用程序使用模式是如何变化或发展的。这也意味着我的手机从 HTC One(带有 Sense Launcher)换成了我最近的 Oneplus(带有 Oneplus Launcher)。你也可以注意到,我已经从 Whatsapp 转移到了 Signal messenger。

总体每日使用趋势

# Overall Daily usage trendme %&#x3E;%
filter(!str_detect(header,&#x22;com.&#x22;)) %&#x3E;%
filter(as.Date(time_ist) &#x3E;= as.Date(&#x22;2017-01-01&#x22;)) %&#x3E;% 
group_by(Date = as.Date(time_ist)) %&#x3E;%
count(n = n()) %&#x3E;%
ggplot(aes(Date,n, group = 1, color = &#x22;red&#x22;)) +
geom_line(aes(alpha = 0.8),show.legend = FALSE) +
stat_smooth() +
# Courtesy: [https://stackoverflow.com/a/42929948](https://stackoverflow.com/a/42929948)
scale_x_date(date_breaks = &#x22;1 month&#x22;, date_labels =  &#x22;%b %Y&#x22;) + 
labs(
    title = &#x22;Daily-wise usage&#x22;,
    subtitle = &#x22;2+ years (including some 2019)&#x22;,
    x = &#x22;Months&#x22;,
    y = &#x22;# of Interactions&#x22;
  ) + theme(axis.text.x=element_text(angle=60, hjust=1))+
  theme(legend.position=&#x22;none&#x22;) +
  ggthemes::theme_hc(style  = &#x22;darkunica&#x22;)

给出了这个图:

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

这个情节最让我害怕。自从我买了一部新手机后,我的手机使用量真的激增了,这似乎不是我工作效率的好迹象。

工作日与周末

这个情节是为了看看我是否真的是一个电话上瘾者,即使是在家里和家人在一起。

me %>% 
  filter(!str_detect(header,"com.")) %>% 
  group_by(Date = as.Date(time_ist)) %>% 
  count(n = n()) %>% 
  mutate(year = as.factor(year(Date)),
         weekday = weekdays(Date, abbr = TRUE)) %>% 
  mutate(what_day = ifelse(weekday %in% c("Sat","Sun"),"Weekend","Weekday")) %>% 
  filter(year %in% c(2017,2018)) %>% 
  group_by(year,what_day) %>% 
  summarize(n = mean(n)) %>% 
  ggplot(aes(fct_relevel(what_day, c("Weekday","Weekend")),
             n, group = year, color = year)) + 
  geom_line() +
  labs(
    title = "Weekday vs Weekend usage",
    subtitle = "For two years",
    x = "Weekday / Weekend",
    y = "# of Interactions"
  ) +
  ggthemes::theme_excel_new()

给出了这个图:

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

幸运的是,事实证明我并没有像我担心的那样上瘾。

消息使用

多年来,我使用过从普通短信到即时消息的各种消息应用。

# Messaging Usage
p <- me %>% 
  filter(str_detect(tolower(header), regex("signal|message|whatsapp"))) %>% 
  mutate(ym = as.Date(paste0(format(as.Date(time_ist),"%Y-%m"),"-01"))) %>% 
  group_by(ym) %>% 
  count() %>% 
  #https://community.rstudio.com/t/tweenr-gganimate-with-line-plot/4027/10
  ggplot(aes(ym,n, group = 1)) + geom_line(color = "green") +
  geom_point() +
  ggthemes::theme_hc(style = "darkunica") +
  theme(axis.text.x = element_text(colour = "white",
                                   angle = 60),
        axis.text.y = element_text(colour = "white")) +
  scale_x_date(date_breaks = "1 month", date_labels =  "%b %Y") + 
  labs(
    title = "Messaging usage",
    x = "Year-Month"
  ) +
  transition_reveal(ym) + 
  ease_aes('cubic-in-out')
animate(p, nframes = 20, renderer = gifski_renderer("msging.gif"), width = 800, height = 800)

给出此动画:

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

这张图表显示了这是我总体手机使用量的驱动因素之一。相似时期的相似峰值。

Youtube 的使用

# YouTube Usage
yt <- me %>% 
  filter(header %in% "YouTube") %>% 
  mutate(ym = as.Date(paste0(format(as.Date(time_ist),"%Y-%m"),"-01"))) %>% 
  group_by(ym) %>% 
  count() %>% 
  #https://community.rstudio.com/t/tweenr-gganimate-with-line-plot/4027/10
  ggplot(aes(ym,n, group = 1)) + geom_line(color = "red") +
  geom_point() +
  ggthemes::theme_hc(style = "darkunica") +
  theme(axis.text.x = element_text(colour = "white",
                                   angle = 60),
        axis.text.y = element_text(colour = "white")) +
  scale_x_date(date_breaks = "1 month", date_labels =  "%b %Y") + 
  labs(
    title = "YouTube usage",
    x = "Year-Month"
  ) +
  transition_reveal(ym) + 
  ease_aes('quintic-in-out')
#anim_save("yt.gif", yt , width = 600, height = 600)
animate(yt, nframes = 10, renderer = gifski_renderer("yt2.gif"), width = 800, height = 800)

给出此动画:

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

这是我在 Youtube 上的使用情况,我主要使用媒体内容,这也与我的总体手机使用情况非常一致,这意味着这可能是另一个潜在的驱动因素。可能是我的手机屏幕变大了,所以我喜欢看更多的视频😑我也不希望事情变成这样。

结论

虽然我一直认为自己是为数不多的数字极简主义者之一,但这份分析证明,我并不完全是数字极简主义者,但在减少手机使用和改善生活方式方面,我还有待努力。请注意,这篇文章是以烹饪书风格而不是教程风格写的,这样你就可以开始运行你的 Android 活动分析。如果您对代码(逻辑)有任何疑问,请随时在评论中提出,我很乐意澄清它们。希望这篇文章能帮助你进行数据驱动的自我反省——至少是 Android 手机的使用。

参考

  • 如果你对学习处理 web 数据感兴趣,可以看看这个关于处理 Web 数据的教程
  • 整个代码库(还有一些章节和情节)可以在我的 github 上找到。随意星/叉使用!

本帖最初发表于 DS+

角度和散景

原文:https://towardsdatascience.com/angular-and-bokeh-e8acd86e7ab1?source=collection_archive---------12-----------------------

我们最近遇到了在应用程序或网站中显示质量图表的问题。但是除此之外,您还希望能够从 python 后端发送更新事件,并拥有所有漂亮的交互,比如按钮按压和文本输入事件,对吗?

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

散景图组件可能并不总是最佳的解决方案,但是尽管如此,我们希望在 GitHub 知识库中与您分享我们认为是一个很好的、最小的例子,演示了如何将 python 后端集成到 angular 应用程序中。

起点

是我们集成到 Angular 项目中的 BokehJS 库。绘图数据由 websocket 服务提供,在我们的示例中,我们使用 aiohttp,但是您可以设置任何其他 websocket 连接。角度组件可以通过它的标签名集成到 html 中的任何地方,下面的代码片段显示了散景图组件

<bokeh-chart></bokeh-chart>

散景图组件是一个规则的角度组件,有一个 html 部分

<div [id]="id"></div>

和打字稿部分。图表组件只需要向它自己的 html 部件提供 id。图表的数据由一个服务提供,这个服务在 ngOnInit 中的组件初始化时被调用。散景图组件的相关 typescript 部分如下所示

...
export class BokehChartComponent implements OnInit {
  public id: string;

  constructor(
    private bokehService: BokehService) { }

 ngOnInit() {
     this.id = "chart";
     this.bokehService.getChart(this.id);
 }
}

由于 BokehJS 库没有可用的类型,angular 中的集成并不像它应该的那样简单。人们只能通过库的全局暴露对象来访问库,在这种情况下,它也被命名为 Bokeh ,并且它是嵌入图表所必需的唯一挂钩。

// this is the global hook to the bokehjs lib (without types)
declare var Bokeh: any;

只有当你将普通的 java 脚本插入 angular 应用程序index.html最顶层的 html 文件时,这种魔力才会如预期的那样发挥作用

<head>
 ...
  <link
    href="[https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css](https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css)"
    rel="stylesheet" type="text/css">
  <script src="[https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js](https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js)"></script>
 </head>

博客服务

通过 MessageService 为图表提供数据,MessageService 封装了与后端的连接,因此只需通过公开的方法 sendMsg(msg)发送适当格式的消息。

export class BokehService extends Connector {constructor(private msgService: MessageService) {
 super(‘BokehService’);
 this.msgService.register(this);
 }…public getChart(id: string) {
 const msg = {
 name: ‘addChart’,
 args: [id],
 action: ‘default’
 };
 this.msgService.sendMsg(msg);
 }

该服务还向后端公开了一个方法,该方法实际上将图表绘制到原生 DOM 元素中,我们首先必须删除之前的绘图。

public plot(msg: Message) {
      const id = msg.args.id;
      const el = document.getElementById(id);
      // first remove the previous charts as child
      // like this, bokeh does not let us update a chart
      while (el.hasChildNodes()) {
            el.removeChild(el.lastChild);
      }
      // be sure to include the correct dom-id as second argument
      Bokeh.embed.embed_item(msg.args.item, id);
    }

后端服务

在我们的例子中是用 python 写的。我们使用 aiohttp 作为 web 服务器的异步解决方案。在浏览器中启动 angular 应用程序后,angular WebsocketService 立即连接到服务器端的 python 后端。请记住,在生产中,您将在这一点上实现更多的安全性,比如身份验证。后端准备接收来自 angular 的事件,例如给我散景图的数据。

来自 angular 的消息调用的 addChart 将 chartItem 作为连接到 websocket 服务的 json 项发送

 async def addChart(self, id_, user):
        """
        Example for adding a bokeh chart from backend

        """
        chartItem = self.chartProvider.chartExample()
        print("try to add chart for dom-id %s" % id_)
        context = {"name": "BokehService",
                   "args": {"item": chartItem, "id": id_},
                   "action": "plot"}
        await self.send_event(json.dumps(context), user=user)

这里有趣的部分是 send_event 方法,它实际上是基于我们的 websocket 服务器的实现。如前所述,在您的具体实现中,该部分可能会有所不同。

图表的最小示例也是作为 ChartProvider 类的成员函数编写的,它看起来非常简单,只为散景中的普通正弦图生成数据

import time
import numpy as np
from bokeh.plotting import figure
from bokeh.embed import json_itemclass ChartProvider(): def chartExample(self):
        t0 = time.time()
        # prepare some data
        self.phi += 0.02
        x = np.arange(0., 10., 0.1)
        y = np.sin(x + self.phi)
        # create a new plot
        p = figure()
        p.line(x, y, legend="SIN")
        chart_item = json_item(p)
        print(time.time()-t0)
        return chart_item

动画图表:可视化 R 中的“变化”

原文:https://towardsdatascience.com/animated-charts-visualizing-changes-in-r-fee659fbabe5?source=collection_archive---------19-----------------------

改头换面周一发布了一项来自世界银行的关于世界发展指标的每周数据挑战:青少年生育率(每千名 15-19 岁女性的出生率) 三月份。有了这个数据集,首先我想看看这些数字是如何随时间变化的,以及在收入群体方面是否存在模式。本文反映了数据操作的过程以及图表是如何从静态发展到动态的。

线条的变化

当谈到随时间的变化时,折线图是查看结果的快速而清晰的解决方案。

setting <- ggplot(final, aes(Year, Rate, group = Country))setting+ 
 geom_line(alpha = 0.5, aes(color = factor(Income_group))) +
 scale_colour_manual(values = c(high, upmiddle, lowmiddle, low)) + 
 facet_grid(~Income_group) + 
 theme_minimal()

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

正如我所料,几乎每个国家的青少年生育率都在下降。不同收入群体之间的差异在于,高收入国家的青少年生育率在 20 世纪 90 年代下降得相当快;而低收入国家的失业率下降缓慢。情况正在好转,只是在世界的某些地方没有那么快。

视觉上强调“变化”

根据上面的观察,我在想如何将收入群体的快速和缓慢变化可视化。我以 10 年为间隔提取了 1960 年至 2016 年的数据集,并创建了直方图来查看逐年的变化。

# extract data for some years
data.someyears <- final %>% 
 filter(Year %in% c(2016, 2010, 2000, 1990, 1980, 1970, 1960))ggplot(data.someyears, aes(Rate, fill = factor(Income_group))) +
 geom_histogram() +
 scale_fill_manual(values = c(high, upmiddle, lowmiddle, low)) +
 facet_grid( ~Year) +
 theme_minimal() +
 coord_flip()

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

如直方图所示,随着时间的推移,越来越多的国家青少年生育率下降。高收入国家从高税率到低税率有显著的变化;低收入国家只取得一点点进步。然而,从 8 个独立的数据片段中想象这种运动并不容易。

制作动画图表

这一系列直方图让我想起了制作动画图表的想法,这样我们就可以直接“看到”变化。我找到了一篇关于使用 gganimate 库在 R 中制作动画的有用文章。

movinghist <- ggplot(final, aes(Rate, fill = factor(Income_group))) +
 geom_histogram() +
 scale_fill_manual(values = c(high, upmiddle, lowmiddle, low)) +
 theme_minimal() +
 coord_flip() +
 #animation setting
 labs(
 title = ‘Year: {frame_time}’,
 y = ‘Number of countries’, 
 x = ‘Adolescent Fertility Rate (births per 1000 woman between 15–19)’
 ) +
 transition_time(as.integer(Year)) + #show the Year as integer
 ease_aes(‘linear’)animate(movinghist, width = 600, height = 600)

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

动画很有帮助。我们可以看到不同收入群体的国家如何降低青少年生育率。

除了按组可视化变化,我还进一步可视化了单个国家的变化。这样,每个国家都有自己的地方,一个圆点就是一年的青少年生育率。由于有这么多国家,我一次制作了一个收入群体的动画图表。

下面是一组的结果:

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

移动的点显示了有趣的行为。一些国家的利率有升有降。有些有明显的下降。如果用彩色编码,图表会提供更多的信息。因此,我计算了每年数字的变化,用绿色表示减少(因为越少越好),红色表示增加,灰色表示没有变化*(1960 年也是没有变化)*

movingdots <- ggplot(filter(final, Income_group == ‘High income’), aes(Country, Rate, color = factor(Status))) +
 geom_point(alpha = 0.7, size = 2) +
 scale_color_manual(values = c(down, up, same)) +
 facet_grid(~Income_group, scales = ‘free’) +
 theme_minimal() +
 theme(axis.text.x = element_text(angle = 60, hjust = 1)) +
 #animation setting
 labs(
 title = ‘Year: {frame_time}’,
 x = ‘Country’, 
 y = ‘Adolescent Fertility Rate (births per 1000 woman between 15–19)’
 ) +
 transition_time(as.integer(Year)) + #show the Year as integer
 shadow_mark(alpha = 0.3, size = 0.5) +
 ease_aes(‘linear’)animate(movingdots, width= 1800, height=600)

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

我有点沉迷于这些点,继续看它们。

请随时与我分享反馈。

动画信息图形

原文:https://towardsdatascience.com/animated-information-graphics-4531de620ce7?source=collection_archive---------36-----------------------

使用 Python 和 Plotly

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

Image www.pexels.com

各种数据集的动画信息图形是 youtube 上的一个热门话题,例如频道

[## 数据是美丽的

这个频道是我的激情项目,带我们一起沿着记忆之路进行一次有趣的旅行,这样我们就可以重温多彩的…

www.youtube.com](https://www.youtube.com/channel/UCkWbqlDAyJh2n8DN5X6NZyg)

拥有近百万订户。在这篇文章中,我将展示我的数据集上的一些这样的信息图的例子,这些数据集是奥地利报纸上关于政治的新闻文章,以及如何用 Python 和 Plotly(【https://plot.ly/】)创建动画。

在文章https://medium . com/datadriveninvestor/topics-in-online-news-before-the-Austrian-elections-2019-d 981 fac BD 0 DD中,我分析了选举前的话题。在这篇文章中,我想回顾一下从 2019 年下半年开始的新闻,并用动画图形来分析一段时间内的新闻。我收集并分析了 12 家奥地利在线日报的 22,000 多篇新闻文章。

第一个视频显示了每一天每一方被命名的频率。从 7 月开始算,一直算到年底。最后,VP 就在 FP 的前面,“NEOS”显然在最后。

在第二个视频中,显示了每个政党每周在每份报纸上被提及的次数。这也显示了整个下半年的动画。值得注意的是,个别媒体对某些党派有明显的偏好,例如,NEOS 在 ORF 之外很少被提及。国民议会选举的那一周在越来越多的报道和提及中清晰可见。选举后的联合政府谈判时期可以从“绿党”越来越多的命名中看出来。其他很多方面往往要看几遍视频才能看出来。

为了不仅检查报告的数量,我还试图以进一步可视化的方式显示主要内容。最常见的单词每天都显示在“单词云”中。占主导地位的人物明显突出,但选举期间的用词变化也很明显。在这里,像“百分比”或“选举结果”这样的词占主导地位。

让我们看看动画是如何创作的?

我加载了一个包含文章的数据框和一些用于数据框操作和日期处理的包。

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

Dataframe with articles

在统计了文章中提到的政党之后,数据框架被扩展为每个政党一栏。

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

Extended data frame

然后一些政党、报纸和动画日的列表被创建。对每个政党(和报纸)的条目进行汇总,生成一个带有“Plotly”的条形图。这些都标有条目的数量。为了获得更平滑的动画过渡,在两天的条形图之间计算了 15 个中间步骤,这些步骤是线性插值的。

在对图形进行一些格式化和标记后,它们被保存为编号的 PNG 文件。然后可以使用 Adobe After Effects 等软件将这一系列图像组合成一部电影。

为了创建“文字动画”,我使用了 Python 包“文字云”(https://github.com/amueller/word_cloud)。这个包为每天计算一个“单词云”,并再次保存为一系列 PNG 文件。

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

Image by the author

最后,我使用 After Effects 合并了上面显示的视频,并添加了一些字幕。

带 tweenR 的 R 中的动画情节

原文:https://towardsdatascience.com/animated-plots-in-r-with-tweenr-80c48b9f5292?source=collection_archive---------18-----------------------

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

Photo by Markus Spiske on Unsplash

利用动画来展示数据趋势是一个很好的讲述工具。在 R 中有几个创建动画效果的包

我最近遇到了 tweenR,它允许你通过插值创建平滑的过渡。tweenr 包本身实际上并不执行任何动画;相反,它创建中间状态,使动画更流畅。

下面是一个使用一些虚拟数据的教程:

library(dplyr)
library(scales)
library(tweenr)
library(animation)
library(dplyr)
library(ggplot2)
library(ggthemes)# an example of a time series data frame
plot_data <- data.frame(Date =seq(as.Date("2010/1/1"), as.Date("2019/1/1"), "years"), Value=runif(10,0,1000)) # prepare  data for tween by creating columns for the time between points
df.all<-plot_data %>% 
  mutate(day=as.numeric(Date-min(Date)+1),ease="linear")# add rows to fill in the gaps
plot_data_tween<-tween_elements(df.all, time = "day",  group="ease", ease="ease", nframes = nrow(df.all)*20)
df_tween_appear <- tween_appear(plot_data_tween, time='day', nframes = nrow(df.all)*10) 

绘图功能:

make_plot <- function(i){
  plot_data <-  df_tween_appear %>% filter(.frame==i, .age> -3.5) 
  p<- plot_data %>%
    ggplot()+
    geom_line(aes(x=Date, y=Value),color="darkgray", size=0.7)+
    geom_point(data=. %>% 
              filter(Date==max(Date)),
              mapping=aes(x=Date, y=Value),
              size=3,color="pink",stroke=4)+
    geom_point(data=. %>% 
               filter(Date==max(Date)),
               mapping=aes(x=Date, y=Value), color="white", size=2)+
    geom_text(data=. %>% filter(Date==max(Date)),
              mapping=aes(x=Date,
              y=Value,label=round(Value,0)),
              color="black",
              nudge_x=7,hjust=1.5, size=6)+
    geom_line(data=df.all, aes(x=Date,y=Value),alpha=0.1)+
    theme_minimal(base_family = "sans")+
    scale_y_continuous(sec.axis=dup_axis())+
    theme(plot.subtitle=element_text(face="italic",size=14),
          plot.title=element_text(color="darkcyan",size=19),
          plot.caption=element_text(hjust=0),
          panel.grid.major.x = element_line(color="lightgray"),
          panel.grid.minor.x = element_line(color="lightgray"),
          panel.grid.major.y = element_line(color="lightgray"),
          panel.grid.minor.y = element_line(color="lightgray"),
          axis.text.x = element_text(angle = 90, hjust = 1, size=12))+
          labs(x="",y="Total Value",
          caption="All values- 2010-2019.")+
          ggtitle(("Yearly Trends of Values"))
  return(p)
} 

现在我们循环每一帧并创建情节,然后使用动画包的saveGIF函数保存它。

ani_settings<-ani.options(interval=1/10)  *# N*umber to set the time interval of the animation (unit in seconds); *adjust for speed of ani*saveGIF({for (i in 1:max(df_tween_appear$.frame)){
  g<-make_plot(i)
  print(g)
  print(paste(i,"out of",max(df_tween_appear$.frame)))
  ani.pause() # adds a pause the end of animation
}
},movie.name="gifs/yearlytrends.gif",ani.width = 500, ani.height = 350)

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

一个有趣的例子:时间覆盖了多年

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

我摆弄了一下 Kaggle 的**‘时代杂志封面图片’**数据集。

该数据包含每年登上封面的女性和男性人数。然后我给数据框添加了帧和渐变。

调整“make_plot”上方的函数,然后在 tween_elements 参数中按性别分组,以便每年创建一个帧。

结果图如下所示:

性别人口统计

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

我在 R 中找到了一些很棒的动画资源,特别是:tweenr github 有一些教程:https://github.com/thomasp85/tweenr

我使用了这里的优秀教程来学习和理解 tweenR:

http://lenkiefer.com/2018/03/18/pipe-tweenr/

使用 Javascript D3 库的动画讲故事

原文:https://towardsdatascience.com/animated-storytelling-using-the-javascript-d3-library-a1c2264142ad?source=collection_archive---------16-----------------------

D3 是可用的最灵活的数据可视化工具,允许你创建伟大的数据故事插图

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

在我发表这篇文章的当天,英国将在 12 月举行自 1923 年以来的首次大选。代议制政府非常有趣,关于投票的数据往往可以告诉我们一个不同的故事,从下议院席位的最终结果以及哪个政党(或政党联盟)组成政府来看。

受 Nathan Yau 的原创 viz 和这些人的一个奇妙项目的启发——我自由地窃取并调整了代码——我开始创作一个自 1918 年以来所有英国大选的故事(这被认为是第一次现代选举)。我想到了一个相当简单的布局,其中我将有一个由 1000 名合格的左翼选民组成的力导向气泡图,其中选民将从一次选举“移动”到另一次选举,显示大众投票是如何分布的。在右边,我会有一个简单的叙述,有一张当选总理的照片和一个关于发生了什么的简短叙述。这是我最初想做的事情的草图。

如果你不想看“如何做”,我的完整代码在这里是,可视化在这里是。

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

My initial sketch

我需要什么?

对于这个项目,我需要:

  1. 一些数据——我们需要做一些操作,并把它们放在一个data文件夹中。
  2. 一些图片——我从维基百科中提取了所有首相的照片,并把它们放在一个名为img的文件夹中。为方便起见,每个文件都按选举日期命名。
  3. 一些 Javascript,它们会放在一个js文件夹里。
  4. 一些用于造型的css,它们将放在css文件夹中。

准备数据

从英国议会图书馆,我能够得到关于各政党的投票百分比的数据,我能够将这些数据与投票率统计数据结合起来,这样我就可以在我的图表中包括那些没有投票的人。所以我的起始csv档是这里的。

我需要重新想象一下,如果有 1000 名选民,每个人都由气泡图上的一个小气泡代表,那会是什么样子。所以我需要做的是将这些统计数据转换成一个 1000 行 27 列的数据帧,每行代表一个选民,每列代表一个选举日期。然后我按照五个党派团体和‘没有投票’的团体给每张选票编码 0,1,2,3,4,5。在我的数据框的每个单元格中,都是那个人在那次选举中投票支持的政党的代码。

然后,我需要向数据中添加一些时间序列,因为它将用于说明人们在时间上的运动。利用启发这项工作的方法,我决定将 27 次选举想象成一天中的几分钟,并将时间平均划分为 1440 分钟,这样最终的可视化将在移动到下一次选举之前显示相同时间的每个选举结果。

最后,我需要将每个人的数据输出为逗号分隔的字符串,其中奇数元素表示政党代码,偶数元素表示时间,因为这将是输入 web 代码的格式。

我在 R 中完成了这个转换,并将输出写成一个tsv文件,稍后我会将它放入我的 web 代码中。我用的 R 代码是这里的这里的最后的tsv输出是这里的这里的

在 D3 中设置 viz

我们需要在我们的js文件夹中有可用的 D3 v3.5.5。我取了美国时间使用 viz 中使用的 Javascript 代码,并根据我的目的进行了调整,创建了一个名为election_bubbles.js的文件。首先,我设置了起始速度和图表尺寸,创建了一个列表供以后使用,并将时钟设置为零:

var USER_SPEED = "fast"; var width = 750,    
    height = 750, 
    padding = 1, 
    maxRadius = 3, 
    sched_objs = [], 
    curr_minute = 0;

接下来,我将每个活动代码定义为一个政党:

var act_codes = [ 
    {"index": "0", "short": "Conservative", "desc": "Conservative"},     
    {"index": "1", "short": "Labour", "desc": "Labour"}, 
    {"index": "2", "short": "Liberal/Liberal Democrats", "desc": "Liberal Democrats"}, 
    {"index": "3", "short": "Other", "desc": "Other"}, 
    {"index": "4", "short": "Plaid Cymru/SNP", "desc": "Plaid Cymru/Scottish National Party"}, 
    {"index": "5", "short": "Did not vote", "desc": "Did not vote"},
];

我还以毫秒为单位设置了我的速度选项,供以后使用:

var speeds = { "slow": 200, "medium": 125, "fast": 75 };

现在我使用了一个名为time_notes的列表来保存可视化所需的所有显示信息。对于每个选举,我创建了如下的键值对:

  • start_minute:我们的“虚拟时钟”上开始每次选举显示的时间——53 的倍数加 1。(53 是 1440/27 的舍入版本)。
  • stop_minute:停止每次选择显示的时间——在start_minute之后 50 虚拟分钟。
  • year:选举日期显示在页面顶部。
  • img:当选首相的照片之路。
  • color:包围每个img的边框——对应总理所属政党的颜色。
  • note:要显示的文本叙述。

这是一个相当长的列表,所以我不会在这里显示它—您可以在链接的源代码中看到它。此外,我将设置一个变量notes_index = 0,在后面的代码中指示我想从列表的第一个元素开始——1918 年的选举。

接下来,我想将“没有投票”的位置设置在 viz 的中心,然后将五个政党分组均匀地分布在中心周围的一个圆圈中。我希望列表中的第一方位于圆圈的右上位置,并将使用我的索引来确保:

var center_act = "Did not vote", 
    center_pt = { "x": 380, "y": 365 }; var foci = {};
act_codes.forEach(function(code, i) { 
    if (code.desc == center_act) {  
        foci[code.index] = center_pt; 
    } else {  
         var theta = 2 * Math.PI / (act_codes.length-1);    
         foci[code.index] = {x: 250 * Math.cos((i - 1) * theta)+380, y: 250 * Math.sin((i - 1) * theta)+365 }; 
    }
});

现在我将开始初始的svg——我计划在最终将它放入 HTML 时,将它完全放在它的父节点中:

var svg = d3.select("#chart").append("svg")    
    .attr("width", width)    
    .attr("height", height)    
    .attr('position', 'absolute')    
    .attr('left', '200px')    
    .attr('top', '200px');

运行 viz

现在,我将编辑主要的 viz 函数,以在每次选举的原力导向网络中移动。本节中的所有代码都将封装在一个函数中,该函数将在我们的 1000 个投票者的tsv文件上运行,如下所示:

d3.tsv("data/elec_results.tsv", function(error, data) {}

首先,我们从tsv中获取投票活动和持续时间的键值对列表,这些将填充到sched_objs列表中:

data.forEach(function(d) {  
    var day_array = d.day.split(",");  

    var activities = [];  
    for (var i=0; i < day_array.length; i++) {   
        if (i % 2 == 1) {    
            activities.push({'act': day_array[i-1], 'duration': +day_array[i]});   
        }  
     }  
     sched_objs.push(activities); 
});

接下来,我们只是窃取代码来填充每次选举的 1000 个节点,并将它们与标签一起写在我们的初始svg上。我不会在这里全部复制,但您可以在第 99–175 行这里找到它。

现在,我们使用timer()函数在后台运行虚拟时钟时运行数据中的开关。你可以在第 179–294 行这里找到完整的timer()函数,但是在这个函数中,我想强调一些这个可视化特有的东西。

首先,我们使用 D3 的transition()函数滚动图表标题中的日期,通过从背景色淡入使文本快速显示(这是您第一次看到某些样式元素,如背景色和字体):

 if (true_minute == time_notes[notes_index].start_minute) {     
    d3.select("#year")          
    .style("color", "#fffced")          
    .style("text-align", "left")          
    .style("font-size", "300%")    
    .style("font-family", "adobe-caslon-pro")   
    .text(time_notes[notes_index].year)    
    .transition()    
    .duration(500)    
    .style("text-align", "center")    
    .style("color", "#000000");  
}

同样,我们可以通过转换图像的不透明度来淡入和淡出图像:

if (true_minute == time_notes[notes_index].start_minute + 10) {
d3.select("#image").append('img')
    .attr('src', time_notes[notes_index].img)
    .attr('width', 200)
    .attr('height', 250)
    .style('position', 'absolute')
    .style('top', '100px')
    .style('left', '150px')
    .style('opacity', 0)
    .style("display", "block")
    .style("background", time_notes[notes_index].color)
    .style("padding", "8px")
    .style("border", "1px solid #ccc")
    .style("box-shadow", "5px 5px 5px #999")
    .transition()
    .duration(1000)
    .style('opacity', 1);
}

我们也可以上下滚动音符:

if (true_minute == time_notes[notes_index].start_minute + 10) {
   d3.select("#note")
      .style("top", "500px")
      .style("color", "#fffced")
      .style("font-size", "150%")
      .style("font-style", "italic")
      .transition()
      .duration(500)
      .style("top", "370px")
      .style("color", "#000000")
      .text(time_notes[notes_index].note);
}

您将看到许多类似的代码段,它们根据力导向气泡图中运动的时间来处理文本和图像的出现和消失。此外,还有一个功能可以根据英国政党的官方颜色来设置节点的颜色。

建立 viz

我们的 Javascript 现在给了我们几个动画对象来插入我们的网页。

  • 一个选举日期(#year),我想把它放在顶部,居中,在其他标题文本的下面
  • 一个力导向的气泡图(#chart),我想在页面的左边。
  • 一个图像(#image),我想在右边,一些叙事文本上方居中
  • 一些叙事文本(#note),我想在右边,居中,图像下方。

一些简单的html和一些css样式可以在css文件夹中找到——这里是为我工作的一般设置:

<div class="container" style="width:1600px">
    <div class="row">
        <img src="img/commons_logo.png" width="400px" class="center">
    </div>
    <div class="row">
        <div style="text-align:center;font-family:adobe-caslon-pro;font-size:200%;font-weight:bold">A Century of UK General Elections</div>
    </div>
    <div class="row">
        <div id="year"></div>
    </div>
    <div class="row">
        <div id="chart" class="col-lg-2" style="position:relative;width:1000px"></div>
        <div class="col-lg-2" style="position:relative;width:500px">
           <div id="image"></div>
           <div id="note" style="text-align:center"></div>
        </div>
    </div>
    <div class="row">
        <footer style="font-size:25%;text-align:right">Created by @dr_keithmcnulty. Github: <a href="https://www.github.com/keithmcnulty/uk_election_bubble">keithmcnulty/uk_election_bubble</a>. Source: UK Parliament, Statistica, Wikipedia</footer>
    </div>
   <div class="row" style="text-align:center">
        <div id="speed">
        <div class="togglebutton slow" data-val="slow">Slow</div>
        <div class="togglebutton medium" data-val="medium">Med</div>
        <div class="togglebutton fast current" data-val="fast">Fast</div>
    </div>
    </div>
    </div>
</div>

我将它插入到electionvis.html中,这也调用了一些引导样式和我的 JS 脚本。然后将它作为一个iframe插入到主index.html中,如果需要的话,可以控制 viz 的整体高度和宽度。

成品

这是成品的样子:

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

See the full thing here

使用 Javascript 需要一些学习,但是 D3 是一个非常灵活的工具,可以构建数据驱动的故事,在我看来完全值得努力。此外,还有许多预先构建的代码可供您根据自己的目标重新使用,就像我在这里所做的一样。

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedInTwitter上找我。

用 StyleGAN 制作 gAnime 动画:第 1 部分

原文:https://towardsdatascience.com/animating-ganime-with-stylegan-part-1-4cf764578e?source=collection_archive---------8-----------------------

介绍与创成式模型交互的工具

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

Figure 1: Modifying spatial map(s) at a single location to produce an animation

1.1:前言

这是一个关于我使用生成对抗网络进行的一个项目的技术博客。由于这是一个个人项目,我摆弄了一个我通常不会在专业环境中使用的动画数据集。以下是该数据集的链接以及关于使用该数据集的模型的详细介绍:

[## Danbooru2018:大规模众包、标签化的动漫插画数据集

用于计算机修订的深度学习依赖于大型标注数据集。分类/归类受益于…

www.gwern.net](https://www.gwern.net/Danbooru2018) [## 用 StyleGAN 制作动漫脸

生成神经网络,如 GANs,多年来一直在努力生成质量不错的动漫人脸,尽管…

www.gwern.net](https://www.gwern.net/Faces)

我做的大部分工作纯粹是为了学习,但我最终得到的一些更有趣的结果是矩形肖像的嘴部动画:

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

Figure 2: Quick/Smooth mouth animation. Due to lack of data, male portraits tend to be lower quality

我将详细介绍我所做的技术细节和我学到的一些经验。该项目的一部分是一个工具,使互动和学习的生成性敌对网络更容易,但在这一点上,它不是用户友好的。如果我继续这个项目,我的目标之一将是发布该工具的一个版本,任何人都可以立即开始使用它来创建如图 2 所示的动画,但现在它主要是一个研究工具:

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

Figure 3: A screenshot of part of the tool. The UI definitely needs work, but at this point it’s still a prototype that I’m adding and removing features from on a regular basis

我发现,将实验代码整合到这样的工具中,而不是使用 jupyter 笔记本,可以更容易地用不同的设置重复实验。有些概念只有通过重复才能真正坚持,所以如果没有这个工具,我觉得我会错过博客中提到的一些见解。如果你只是对示例动画感兴趣,而不是技术细节,你可以跳到结果:动画部分。

个人项目的一个主要问题是它们只涉及单一视角。我写这个博客的目的是获得其他人对这个话题的看法,详细描述我在这个项目上的工作经历,并接受建设性的批评/纠正。

1.2:简介和结果总结

几年来,我一直习惯于定期重新实现关于生成模型的论文,所以我在 StyleGAN 论文发表的时候开始了这个项目,从那以后一直断断续续地在做这个项目。它包括 3 个主要部分:

1.StyleGAN 的重新实现,做了一些调整

2.使用该实现训练的模型

3.一种用于可视化和与模型交互的工具

我开始重新实现 StyleGAN 作为学习练习,因为当时官方代码还不可用。结果比我使用的其他模型好得多,所以我想更深入地研究。让我兴奋的一个生成模型的应用是视频游戏的自动资产创建。StyleGAN 是我实现的第一个在视频游戏中可以接受的模型,所以我最初的步骤是尝试让 Unity 这样的游戏引擎加载该模型。为了做到这一点,我做了一个. NET DLL,它可以与模型交互,理论上可以导入到 Unity 中。为了测试 DLL,我创建了一个与它交互的工具。当我想到它们的时候,我最终给马具添加了越来越多的特性,直到它成为项目中最大的部分之一。总体架构如下:

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

Figure 4: From TensorFlow python implementation to generating images with the tool

我喜欢使用工具来可视化数字对象并与之交互,否则这些数字对象可能是不透明的(如恶意软件和深度学习模型),所以我添加了一个功能,即功能图(图 5)的可视化以及修改它们的能力。观察不同图像中不同层的特征地图最活跃的位置,有助于我理解模型在做什么,并使自动定位一些面部特征变得简单。当谈到修改时,我注意到在特定位置从特征图中增加或减去值可以用来进行有意义的改变——例如张开和闭上嘴(图 2)。结合自动面部特征检测,这可用于对所有生成的图像进行一致、有意义的修改,而无需标签(图 9,10)。

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

Figure 5: Feature maps can be used to identify meaningful locations (top: eyes, bottom: faces). Red squares indicate negative activations, green squares indicate positive activations, white squares indicate activations close to 0

总结一下:一个特征使用特征映射来修改面部特征。

以下是博客其余部分的结构:

  • 生成模型的应用
  • 工具生成的动画
  • 代码的简要讨论
  • 重新实施详细信息
  • 对数据的讨论
  • 培训程序的讨论

应用程序

总的来说,有几个应用让我对创成式模型感兴趣:

  • 更好的游戏资产生成过程
  • 让艺术创作变得更容易、更快速
  • 无监督学习的基本潜力与生成因素的解开相结合

游戏资产更好的程序生成

我对生成模型将程序生成带到下一个层次的可能性感到兴奋。据我所知,基于现代规则的过程化生成技术不能从高度复杂的分布中随机创建样本。例如,程序生成的关卡的一个子部分可以独立于该关卡的其余部分,并且仍然可以被玩家接受。随机生成图形(如人物肖像)要困难得多,因为好看的图像往往反映真实世界,而真实世界有如此多的相互依赖性,以至于每个像素都需要在每个其他像素的背景下考虑。即使是风格化/绘制的图像也需要一致的照明、解剖、纹理、透视、风格等。为了好看。这也适用于自动生成音频、对话、动画和故事情节。生成模型是目前我所知道的从如此复杂的发行版中可靠地创建样本的最佳方式。

让艺术创作更简单快捷

在生成模型的背景下,交互式工具也有可能允许外行创建图像,否则需要有经验的艺术家,或者允许艺术家加速他们工作中更常规的部分。我不认为生成模型会很快消除对创意艺术家的需求,因为生成模型(以及大多数机器学习模型)专注于对特定分布进行建模。这使得创建不同于训练分布中任何样本的高质量图像(创造性图像)变得困难。像这篇博客中使用的工具,允许人们添加定制的改变,可以帮助产生更独特的图像,特别是如果模型已经学习了一些基本的图形概念,如照明和透视。

无监督学习的基本潜力与生成因素的解开相结合

由于未标记的数据远远超过标记的数据,并且深度学习非常渴望数据,我认为无监督/半监督学习有可能在未来几年逐渐取代监督方法。特别是一些深度生成模型的方法已经被用来理清数据集中的变异因素:关于 StyleGAN 如何做到这一点(至少是部分做到)的例子,见图 7,8。实际上并没有一个一致同意的解纠缠的正式定义,我的理解是有限的实验证据表明它对下游任务有用。然而,与 GANs 一起工作使我乐观地认为这将是有用的。虽然生成器不能为它没有生成的图像生成传统的内部表示,但有几个其他类型的生成模型可以在视觉质量上与 GANs 竞争,并且可能更适合下游任务。

结果:动画

我能训练的最好的模型是在一个动漫数据集上(https://www.gwern.net/Danbooru2018)。我将在数据部分探讨这种做法的优点和缺点,其中一个主要缺点是缺乏多样性:很难塑造男性形象。以下所有示例都是使用该工具生成的。我最初在 jupyter 笔记本上创建了这样的图像,但是使用专用工具大大加快了速度,并让我从不同的角度了解了模型的工作原理。下面的图片根据制作的复杂程度大致排序:图 7/8 比图 6 需要更多的工作,如果没有工具,图 9/10 会比图 7/8 更难制作。

图 6 是几个图像的中间潜变量之间的插值的例子。GANs 可以实现的一个很酷的结果是确保插值图像具有与端点相似的质量。

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

Figure 6: Interpolating between random latent vectors

图 7 是通过在中间潜在空间中定位具有特定含义(在这种情况下是头发或眼睛颜色)的矢量并在该方向上移动来修改图像的例子。例如,黑头发的向量可以通过拍摄许多有黑头发的脸的图像并平均它们的潜在值,然后从所有其他图像的平均值中减去结果来找到。我将在培训/后处理部分详细讨论这一点。

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

Figure 7: Changing a latent vector in a meaningful direction

图 8 显示了应用于属性“mouth open”时用于生成图 7 的相同概念。这在某种程度上是可行的,但是属性并没有完全解开:图像的每个部分都可以看到变化,而不仅仅是嘴巴。常识告诉我们,一个人可以移动他们的嘴,而不会明显改变其他东西。一个简单的方法是将动画嘴部粘贴到其他静态图像上,但这在改变“嘴部张开”向量也会改变肤色或图像风格的情况下不起作用。

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

Figure 8: Vector to change mouth also changes other attributes

图 9 示出了修改靠近角色嘴的空间位置处的特定特征图以产生不会引起全局变化的说话(或咀嚼)动画的示例。只需一点手动工作,就可以找到需要修改以产生这种变化的要素地图,而只需要几个已标注数据的示例。所有这些都可以通过工具来完成。该过程的灵感来自于 DCGAN 论文如何通过修改特征图来演示窗口删除。修改是对特定特征地图的局部区域的简单增加或减少。我将在以后的博客中展示如何使用该工具实现这一点。

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

Figure 9: Spatially local modification to produce talking animation. Once feature map(s) that correspond to a meaningful change are found, they can be applied to most images, regardless of quality or style.

图 10 显示了与图 9 相同的内容,除了应用于发带的存在/不存在。这种技术可以应用于许多不同的属性。

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

Figure 10: Adding/subtracting hairband using local modifications

结果:代码

鉴于我有限的空闲时间和我的主要目标是学习,我把对大多数项目很重要的几个方面放在次要位置:

  • 规划/设计:我从尽可能少的规划开始,并根据我的想法添加功能/变化
  • UI 设计:我使用了贪婪的方法来添加我想到的特性。
  • 代码风格:我并没有带着让别人阅读代码的意图进入项目,相反,我的目标是尽可能快地得到结果。有了恶意软件逆向工程的背景,我并不特别担心需要自己调试低质量的代码。对于工具来说,快速运行效果很好,但对于实现深度学习模型来说,我要慢得多,因为错误很难捕捉和调试。代码质量远非专业项目所能接受的。

有了这些免责声明,这里是 gitub 链接。他们正在积极开发中,所以在一些提交中可能会有一些错误:

StyleGAN 重新实施:

[## Nolan-dev/style gan _ re implementation

这是 NVidia 的风格甘 https://github.com/NVlabs/stylegan 我出于学习目的的重新实现。我的…

github.com](https://github.com/nolan-dev/stylegan_reimplementation)

工具:

[## 诺兰·德夫/甘界面

这是一个与 StyleGAN 模型生成的图像进行交互的工具。它有 3 个部分:TensorflowInterface: Native…

github.com](https://github.com/nolan-dev/GANInterface)

1.3:实施细节

在这一节中,我将详细介绍我重新实现 StyleGAN 并使用它训练一个模型的技术细节

StyleGAN 重新实现

论文发表后不久,我就开始重新实现 StyleGAN,这是在正式代码发布前的几个月。在本节中,我将讨论我遇到的一些挑战和采取的方法,前提是熟悉 StyleGAN 论文和 TensorFlow。

StyleGAN 基于 PGGAN ,我已经重新实现了它。这些模型使用“渐进式增长”,其中鉴别器和生成器在训练期间增长,以处理越来越高的分辨率。开发一个模型有点不常见——我实现的所有其他模型都不需要在训练期间改变它们的架构。幸运的是,TensorFlow 有一些方便的功能,例如能够只为模型的一部分加载保存的权重,并随机初始化其余部分。这也用于迁移学习。

我非常喜欢 TensorFlow 2.0 将模型创建为从 tf.keras.Model 继承的类的风格。我以这种方式创建了大多数层、生成器、鉴别器和映射网络。我还试图让它成为在急切执行和传统的基于图形的执行之间切换的一个选项。急切执行允许更容易的调试,我认为这是更好地理解程序的一种方式(恶意软件分析中的一种常见技术)。不幸的是,当时急切执行似乎比在图形模式下运行要慢得多,所以最终我停止了更新该功能。使用 tf.keras.Model 的一个好处是它对 eager 和 graph 模式都有效,所以理论上让 eager 再次工作应该不会太难。与此同时,我刚刚使用了 tfdebug 命令行界面和 TensorBoard,在这一点上我已经相当习惯了。

StyleGAN 与 PGGAN 有一些重要的不同。有一种名义上的方法,在使用“自适应实例规范化”操作时,将特定于图像的潜在数据作为样式(非空间属性)提供给特征地图。从概念上来说,这种方法实现起来很简单,如本文所述,但我选择使用 tf.nn.moments 来计算均值和方差,其效果不如官方实现版本,后者使用较低级别的运算来计算这些值。我的第一个猜测是,这是由于数字问题,这不是我当时想调试的东西,所以我没有进一步研究它。我通常很乐意更深入地研究这样的问题,因为它们显然是了解更多的机会,但由于这个项目是一个爱好,我必须优先考虑,以充分利用我的时间。

StyleGAN 还使用了一个中间的潜在空间,该空间通过增加潜在值范围的灵活性和依赖性,假设性地(本文中提供了一些经验证据)促进了解开。例如,如果我们假设一个人群中的男性从来没有长头发,那么对应于头发长度的潜在向量应该永远不会在“长头发”区域,而对应于性别的另一个潜在向量在“男性”区域。如果潜在向量是独立的,这是在没有映射网络的情况下发生的,并且我们最终在“男性”旁边采样“长发”,那么生成器将不得不创建一个不是男性或者是短发的图像,以欺骗鉴别者。这意味着即使潜在的头发长度在“长头发”区域,当其他潜在值在它们范围的正常部分时,我们可能最终创建一个短头发的图像。请注意,一些解缠结的定义需要轴对齐(修改单个潜在值会导致有意义的变化),而我的理解是,StyleGAN 的映射网络鼓励中间潜在空间是轴对齐解缠结的旋转(修改潜在值的向量会导致有意义的变化)。

在我看来,使用中间潜在价值就像通过风格将信息融入网络一样有趣。实现起来也很简单。除非另有说明,否则我在本系列博客中提到的潜在价值指的是中间潜在价值。

一篇论文中最初看起来很小的细节变成了最难实现的部分,这种情况非常普遍。StyleGAN 就是这种情况——style gan 和 PGGAN 之间的一个区别是使用双线性上采样(和下采样)和 R1 正则化(对鉴别器的梯度惩罚)。这两者单独实现起来都很简单,但是当我试图将它们结合起来时,结果是 TensorFlow 无法计算 tf.nn.depthwise_conv2d 操作的二阶导数。深度方向卷积用于对每个通道单独应用滤波器。这在卷积神经网络中通常是不需要的,因为(在一些用于移动设备的 CNN 之外)每个滤波器连接到前一层中的所有信道。通常用于实现双线性插值的模糊过滤器一次对一个特征地图进行操作,因此它需要深度方向的卷积。没有二阶导数,我不能计算 R1 罚函数,这需要对梯度求梯度。当时,我对自动微分的了解还不足以让自己轻松实现二阶导数。我花了一些时间试图得到一个更好的理解,然而在这一点上,我已经完成了一切,除了这一部分,不久之后,官方代码被释放。Nvidia 的团队在模糊滤镜中使用了两个 tf.custom_gradient 函数,很好地解决了这个问题。

我对 StyleGAN 做了几次实验性的调整,取得了不同程度的成功。

1.我测试了矩形图像,将初始分辨率改为 8x4,并在此基础上进行增长

2.我试着用 ACGANcGan 和投影鉴别器来调节 StyleGAN

矩形图像效果很好。用 ACGAN 和 CGAN 对标签进行处理则不会。这可能是由于我的超参数选择,但一般来说,结果比没有条件的训练差,然后在潜在空间中找到对应于有意义特征的向量(在训练/后处理部分讨论)。

数据

FFHQ 数据集——收集了 70,000 幅高分辨率图像——与 StyleGAN 论文一起发布。为了更接近生成整个人的目标,我尝试修改 Nvidia 提供的数据提取脚本,以提取 8:4 (h:w)纵横比的图像。除了提取面部,还从面部以下提取了相同数量的数据。将高度和宽度都加倍需要考虑 4 倍的输入尺寸,但是仅将高度加倍就需要 2 倍的尺寸。一个人面部以下的数据也应该比背景数据具有更少的差异(差异主要来自不同的服装),并且不捕捉背景数据意味着网络不需要将容量专用于不属于该人的数据。

不幸的是,在 FFHQ 数据集中的 70,000 幅图像中,只有 20,000 幅图像具有足够的人脸下方数据来创建具有理想纵横比的图像。从图 11 中可以看出,我无法用这个小数据集获得特别高质量的结果,但是可能有提高质量的方法(比如扩展包含图像的标准)。

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

Figure 11: Rectangular FFHQ images tended to be low quality

我也对甘斯在风格化的图画上表演的能力感兴趣,我看到https://www.gwern.net/Danbooru2018最近已经发行。该数据集包含大量高分辨率图像和非常丰富的标签数据。它确实有一些潜在的缺点,例如男性图像的数量少得不成比例,这大大降低了该类图像的质量(图 12)。该图中的男性图像是从生成的大约 1000 幅图像中精选出来的最高质量的图像。我确实认为这里有很大的改进潜力,特别是在普通男性形象周围使用截断技巧。

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

Figure 12: Most male portraits (top) are low quality due to dataset limitations. Even when minimally cherry-picked for quality, female portraits (bottom) tend to be higher quality. None of the images here use the truncation trick for higher quality

该数据集还包含 NSFW 图像的重要部分,尽管我认为生成模型的一个潜在用途是自动修改媒体,使其更适合不同的观众。

为了选择要包含的候选图像,我可以利用元数据,包括“收藏夹”(有多少人收藏了一张图像)、分辨率、标签和创建日期。为了减少差异和提高质量,我排除了很少有人喜欢的图像和 6 年前创建的图像。我还只保留了分辨率至少为 512x256 (HxW)的图像,这是我的模型目标分辨率。最后,我过滤掉了带有标签的图像,这些标签会增加肖像风格数据集中的差异,例如“躺下”和“从侧面”,或者暗示非常 NSFW 的图像。

为了生成数据,我结合使用了以下两种工具,并对其进行了修改,以提取所需纵横比的图像:

[## qhgz 2013/动漫人脸检测器

一种基于快速 RCNN 的动漫人脸检测器。该检测器对 6000 个训练样本和 641 个测试样本进行了训练…

github.com](https://github.com/qhgz2013/anime-face-detector) [## nagadomi/lbpcascade_animeface

使用 OpenCV 的动漫人脸检测器。原始版本自 2011 年起发布于…

github.com](https://github.com/nagadomi/lbpcascade_animeface)

这些工具并不总是正确地提取图像,所以我使用 illustration2vec 来过滤结果,因为无法检测到人的图像可能是坏的。

[## rezoo/illustration2vec

illustration2vec (i2v)是一个简单的库,用于估计一组标签并从标签中提取语义特征向量

github.com](https://github.com/rezoo/illustration2vec)

我还创建了一个工具,可以显示一个大的图像网格,从中我可以快速手动删除坏图像,但对于超过 30,000 张图像的数据集来说,这太耗时了。我最终得到了几个不同的数据集,包括各种质量和标签的图像,图像从 40,000 到 160,000 不等。所有这些都产生了比我构建的 20,000 幅图像 FFHQ 数据集更好的模型。

培训/后处理

我使用每个分辨率多达 80 个时期来训练该模型,其中 PGGAN 过渡阶段分解为 40 个时期,稳定阶段分解为 40 个时期。对于 160,000 的图像数据集来说,这需要一个多月的时间,可能有点过了。我使用 Horovod 在两个 Nvidia Titan RTX 显卡之间分配训练,这允许在早期步骤中的大批量大小,并使我永远不需要低于每批 16 个样本。

为了收集属性,我生成了成千上万的图像,并用 illustration2vec 扫描它们以获得属性估计值。对于有超过 1000 个对应图像的每个属性,我将潜在值平均在一起,并从整体平均图像中减去它们。这就产生了一个向量,当它被加到一个潜在的属性上时,会促进所期望的属性的表达。虽然这工作得很好(我解释为映射网络解开属性的进一步证据),我对改进过程感兴趣,可能通过使用 https://arxiv.org/abs/1907.10786 的中描述的技术。在某些情况下,我试图手动解开相关属性:例如,如果金发与蓝眼睛相关,从金发向量中减去对应于蓝眼睛的向量可能会有所帮助。然后,我将这些属性向量导出到一个 csv 文件中,该文件可以被工具加载。

结论/杂项说明

通过尝试重新实现那些依赖于我不理解的基本原理的论文,然后在清楚的背景下学习这些基本原理,我获得了很多经验。这个过程感觉很像反向传播,意味着我已经花了 30 多个小时试图完全理解早期的一篇论文。这是我第一次尝试创建一个工具来增强这种理解,我认为它可能会成为我前进的标准策略。

我相信视觉工具是提高对主题理解的一个很好的方法。作为一名恶意软件分析师,像 Hiew 这样的工具对我理解恶意软件的方式至关重要,尽管最初并不直观可视化分析如何有助于 windows 可执行文件。鉴于人类视觉系统可用的带宽和处理能力,我的假设是,当用图像表示时,我们可以从大多数具有空间局部结构的数据中快速获得许多洞察力。这也是卷积神经网络似乎能够很好地处理的数据类型(考虑到它们的生物学灵感,这并不令人惊讶)。这个概念也适用于卷积网络本身,这也是我从事这个项目的原因之一:希望可视化卷积神经网络的特征图可以帮助我更好地理解它们。

在这个博客系列的下一部分,我将更详细地介绍这个工具,以及如何用它来制作动画。我还分享了该工具的一个编译版本和一个训练有素的交互模型,因为目前只使用源代码进行交互的过程很复杂。

[## 使用 StyleGAN 制作 gAnime 动画:工具

开源 GAN 研究工具的深入教程

towardsdatascience.com](/animating-ganime-with-stylegan-the-tool-c5a2c31379d)

使用 StyleGAN 制作 gAnime 动画:工具

原文:https://towardsdatascience.com/animating-ganime-with-stylegan-the-tool-c5a2c31379d?source=collection_archive---------9-----------------------

开源 GAN 研究工具的深入教程

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

Visualization of feature map 158 at a layer with resolution 64x64

0.前言

这是我个人项目中的一个研究工具的教程/技术博客。虽然博客的很大一部分假设您在阅读时可以访问该工具,但我试图包括足够多的截图,即使您没有时间亲自尝试,也应该清楚它是如何工作的。

在本教程中,我们将与一个经过训练的 StyleGAN 模型进行交互,以创建如下动画(的帧):

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

Spatially isolated animation of hair, mouth, and eyes

在上面的动画中,改变嘴、眼睛和头发的变换大多是独立的。这通常比用 GANs 制作说话动画的其他方法(据我所知)更可取,这可能会导致脱发等副作用:

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

Another animation we’ll create to demonstrate how changes in the ‘mouth’ attribute can influence other parts of an image. Note the hair thickening and thinning along the edges.

我们还将通过使用网络中各层的特征图来构建简单的启发式面部特征检测器:

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

Using feature maps at various layers to detect mouths, eyes, and cheeks

然后,这些检测器可用于自动进行有意义的修改,以便隔离图像的各个部分:

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

These images generated were in a single batch without human intervention or a labeled dataset

这些步骤都不需要训练集的标签,但是需要一些手工操作。

1.介绍

您可以从以下链接之一下载该工具的编译版本:

[## GanStudio_x64_v1.zip

编辑描述

drive.google.com](https://drive.google.com/file/d/1cv2SiWQKtlC-XCAeAiGHh2C8XCAZe9xd/view?usp=sharing) [## 非常

MEGA 提供免费云存储,具有方便、强大的永远在线隐私功能。立即申领您的免费 50GB

mega .新西兰](https://mega.nz/#!VCIRyIRI!t_g2OQYkuqtPAdd5wgsSHUEYhYI47ip84jydGZMI-bg)

sha 256:ec2a 11185290 c 031 b 57 c 51 EDB 08 BF 786298201 EB 36 f 801 b 26552684 c 43 BD 69 c 4

它配备了一个在动画数据集(T2 的许多项目都基于该数据集)上训练的模型。不幸的是,由于数据集的性质,缺乏性别多样性。我目前正在尝试训练一个模型,以产生更高质量的男性形象,但这需要一段时间才能完成。数据集还包含 NSFW 影像,虽然我生成了数千幅影像,但从未遇到过任何 NSFW,我并没有检查训练集中的每一幅影像(对早期图层中的要素地图进行大规模修改可能会增加风险)。如果你遇到问题,你可以在 https://github.com/nolan-dev/GANInterface提出问题,我会尽力回应。

这个博客有两个部分:基础教程和高级教程。基础教程演示了如何使用该工具,并且不需要太多的技术知识就可以完成(尽管它为感兴趣的人提供了一些技术解释)。高级教程演示了如何定制工具,并且更具技术性——例如,您应该熟悉卷积神经网络中的特征映射

我在以前的博客中介绍了这个工具,并分享了源代码,但是让它工作起来很复杂,需要一个用我的自定义 StyleGAN 实现训练的模型。我希望提供一个工具的编译版本和一个预先训练好的模型能让它更容易试用。该工具是上一篇博客讨论的更大项目(包括 StyleGAN 的重新实现)的一部分,但是阅读它并不是这篇博客的先决条件。如果你感兴趣,这里有一个链接:

[## 用 StyleGAN 制作 gAnime 动画:第 1 部分

介绍与创成式模型交互的工具

towardsdatascience.com](/animating-ganime-with-stylegan-part-1-4cf764578e)

由于这是一个研究工具,我一直在定期添加和减少功能,以更好地了解模型如何工作以及与它交互的最佳方式。有许多次要功能,但主要功能包括:

  • 修改生成图像的潜在向量,以便在图像之间进行插值,表达某些特征,并在质量和变化之间进行权衡(截断技巧)
  • 修改特征地图以改变图像中的特定位置:这可用于动画
  • 读取和处理特征地图,以自动检测有意义的特征
  • 通过创建批处理作业来自动化上述所有操作

和上一篇博客一样,我写这篇博客的目的是获得其他人对这个话题的看法,详细描述我在这个项目上的工作经历,并接受建设性的批评/纠正。本博客的教程格式旨在缓解该工具不发达的 UI,并使其有可能在不处理混乱的源代码的情况下使用。不幸的是,它仅适用于 Windows,但它已在免费的 Windows AWS 实例上进行了测试(Microsoft Windows Server 2019 Base,但图像生成会很慢)。

2.基础教程

在我们开始之前,有一个小提示:我在写这篇文章的时候修改了这个工具,所以一些截图与当前版本略有不同,但是一切都在大致相同的地方。

下载并打开上面链接的 zip 文件后,你会看到几个文件/文件夹:

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

我将在高级部分(3.3)更详细地解释其中的一些,但是此时唯一重要的文件是 GanStudio.exe。当您运行它时,单击免责声明的 ok(希望不要出现任何错误),您将看到如下内容:

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

由于 UI 的复杂性,在本教程中我第一次引用该工具的一部分时,我会有一个附近的截图,相关部分用红色标出。许多 UI 元素都有工具提示。

设置:

使用这个工具涉及到与 windows 资源管理器的交互,用“大”或“超大”图标查看生成的文件是最容易的。通过右键单击资源管理器窗口并选择“查看”来选择其中之一:

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

在许多情况下,按修改日期对图像进行排序也很有帮助,这也可以通过右键菜单实现:

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

生成新图像

要测试图像生成,请单击生成新图像(3)。这将产生一个新的潜在代码,并显示它的图像。注意生成第一幅图像通常比生成后续图像需要更长的时间。

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

该图像是随机创建的,但被插值以接近“平均”图像。这导致更高质量的图像,但减少了变化。如果你把质量滑块(1)放在上图的同一个地方,你的图像很可能是相似的:一个棕色头发,紫色和/或蓝色眼睛的女孩。

加载图像

为了使本教程中的图像与该工具将产生的图像保持一致,我提供了一个示例图像。点击“导入图像”(如上,2)。这将在“肖像”目录中创建一个打开文件对话框。导航至“肖像”上方的目录,并选择“教程文件”:

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

双击 sample_01.png 进行加载。您生成的所有图像都保存在“肖像”文件夹中,您可以使用这种方法再次加载它们。

GANs 通常不能加载任意图像,但是这个工具会将生成图像的潜在代码附加到它写入磁盘的每个 PNG 文件中。“导入图像”按钮读取写入您选择的图像的潜在代码。只要工具加载了生成图像的模型,它就能够重新创建图像。

修改属性

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

Hover over attribute label that have been cut off to see full name

要开始修改属性,请选择“Attributes_0”选项卡(如上)。属性包括头发/眼睛颜色、背景强度、配饰和嘴部状态(微笑/张开)。向右移动对应于属性的滑块将增加该属性对图像的影响,向左移动将减少所述影响。他们中的一些人比其他人工作得更好。选择位置后,按“更新此图像”(如上)。以下是一些例子:

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

Left to right/top to bottom: Positive open_mouth, positive skin_tone, negative background and negative black_hair, positive blonde_hair and negative smile

以这种方式修改属性的一个缺点是它们并不总是空间隔离的;修改只影响图像一部分的属性也会影响其他部分。如果我们想要创建动画,这尤其成问题。要查看实际问题,请执行以下步骤(以下截图供参考):

  1. 向下滚动到“张开嘴”滑块
  2. 把它移到右边
  3. 按“更新此图像”。嘴现在应该微微张开
  4. 选择批次- >属性- >光谱
  5. 选择“确定”以生成 5 幅图像,选择“否”以“滑过 0?”提示。

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

这将产生 5 个图像,其中“张嘴”属性从 0 移动到滑块上的选定位置:

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

将这些内容放入 gif 生成器会生成以下动画:

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

Frame order based on the first number in the filename: 0, 1, 2, 3, 4, 3, 2, 1

正如你所看到的,即使我们只选择了一个与嘴相关的属性,整个图像的特征都会改变。

修改具体位置

在这一节中,我们将只对嘴进行修改,而不改变其他特征。不幸的是,此时使用导入图像按钮导入的图像将不会反映这里所做的更改。

重复加载图像一节中的说明,回到基础图像(或重置属性滑块并更新)。我们使用“空间”选项卡(如下)来修改图像的孤立部分。

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

UI 很复杂,但是对于这一部分,我们将只使用几个部分。我们需要做的第一件事是指出我们想要改变图像的哪一部分:

  1. 选择“嘴巴张开”选项卡。
  2. 放开之前,单击、按住并拖动光标穿过嘴巴

这就在嘴的周围做了一个选择,并确保我们对图像的改变只会影响所选的区域。

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

Click on locations within these squares to create them on your image, or click and drag across this portion of the image

这将产生一个浅绿色的正方形,除非选择了“在可视化中用绿色替换蓝色”。我将在本教程中选择这个选项来提高可见度,并希望当我们开始处理表示负面影响的红框时,对色盲更加友好。

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

如果您错误地选择了一个位置,您可以在选择时按住“control”来删除方块:

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

Remove an undesired selection. Click and drag with ctrl held to remove multiple selections.

以下是您可以在图像上“绘制”的所有方式。其中一些现在还不需要,但以后会有用:

  1. 左键单击产生一个大小取决于所选分辨率的框(稍后将详细介绍)。该框表示在该位置的积极影响,根据您的设置,该框将为绿色或蓝色。
  2. 右键单击在该位置产生一个具有负面影响的红色框
  3. 按住 Ctrl 键并单击可擦除某个位置的方框
  4. 执行上述任一操作时,单击并拖动鼠标以绘制一个大矩形区域
  5. 如果未选中“无选择重叠”,您可以多次选择同一位置以增加选择的幅度(正或负)。这表现为更高的颜色强度和更厚的方框。

选择嘴部后,向右移动“嘴部张开”标签下方的滑块,直到左下方的数字大约为 100。此滑块是“要素地图乘数滑块”,当它向右(积极影响)或向左(消极影响)移动时,会对活动选项卡产生指数影响。将滑块左下方的数字设置为 100 左右,选择“更新此图像”:

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

这应该会产生以下图像:

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

正如标签的名字所暗示的,这开了口。让我们尝试用这种方法制作动画。selectBatch->Fmap->combinator IC(我会在高级教程中详述为什么这么叫):

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

为要生成的图像选择 5:

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

选择 0 作为起点。该批将由 5 个图像组成,滑块在起点和终点之间有规则的间距(0,20,40,80,100)。因为在此图像中嘴默认是闭合的,所以起点 0(无影响)表示闭合。

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

这将产生比属性方法具有更少空间纠缠的 5 个图像:

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

gif 生成器产生以下内容:

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

其他选项卡也可以使用相同的过程,并且可以组合不同的选项卡。如果你使用的是组合批处理生成器,你需要将除激活标签之外的所有标签的乘数条保持为零,以避免产生多个标签的组合。这可以通过在更改活动标签的乘数之前按下“全部设置为零”来完成:

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

这里列出了一些可能的变化。请注意,使用大乘数很可能会产生奇怪的伪像:

  1. 红色或蓝色的眼睛

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

起点和终点如下:

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

The dialog actually says Start Point, this is an old screenshot. The start point corresponds to the first image that gets generated in the batch, and the end point corresponds to the last.

生产:

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

2.脸红

设置:

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

If the screenshot and the tool disagree, believe the tool. This is an old screenshot (should say Start Point instead of End Point)

动画:

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

3.束发带

设置:

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

I actually had the variable for this prompt called ‘startValue’ originally, don’t know why I made the prompt say End Point. You may have guessed this is an old screenshot

动画:

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

4.头发

设置:

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

You’d think it would make sense to start at the low number and end at the high number, but actually this is an old screenshot and End Point should be Start Point. It will start at 100 and shift down to -257

动画:

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

These settings slightly influence the mouth because the effective receptive field of convolutions late in the network cover a lot of spatial locations in early layers.

5.ice_is_nice(???)

设置:

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

We’re selecting the entire image here. For scenarios like this, I’d advocate for the click and drag method over clicking 4096 times with extreme precision. Also: it should say Start Point instead of End Point

动画:

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

Example of a modification late in the network: details are influenced, but overall structure stays the same

其他可以尝试的东西:

  • 批量生成新图像,质量和属性栏位于不同位置:

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

  • 使用“设置为基础图像”(如下)使质量条在当前图像(而不是平均图像)和新图像之间插值。当与 Batch- > New latents (上图)结合使用时,这对于查找与当前图像略有不同的新图像非常有用。

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

  • 使用“切换上一张”在当前图像和上一张图像之间快速切换,以检查更改。

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

Focus on the red box, not the blue box around ‘Toggle Show Selection’ which I clicked earlier. At the moment toggle show selection doesn’t even work.

  • 使用 Misc- >在两幅图像之间插值生成两幅现有图像之间的图像光谱。

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

3.高级教程

本节假设您对卷积神经网络和 StyleGAN 有所了解。

3.1 寻找新功能

“空间”部分中的选项卡(嘴巴张开、发带等)对应于添加到特定图层的特定要素地图的值。尝试选择 mouth_open 选项卡。在选项卡上方的组合框中,它应该显示分辨率:16x16。StyleGAN 中的早期图层具有低分辨率的要素地图,而后期图层具有高分辨率的要素地图(分辨率通常会翻倍)。由于我们生成的图像是 256x256 像素,对应于 16x16 的层在网络中处于早期。要查看 mouth_open 选项卡修改了哪些特征映射,请在选中“过滤零点”的情况下按“查看所有 Fmap Mults ”,然后选择“特征映射输入”选项卡:

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

这意味着通过单击在图像中选择的空间位置会乘以-2,然后乘以要素地图乘数滑块,结果会添加到分辨率为 16x16 的图层上的要素地图 33 中。

一些选项卡会影响多个要素地图:

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

The feature maps influenced by the ‘hairband’ tab, viewed by clicking on ‘View All Fmap Mults’

我通过摆弄这个工具手动找到了这些乘数。我用了两种方法:

  1. 为了修改图像中的现有属性(例如,嘴),我通过修改不同的特征图并查看哪一个产生了期望的结果来使用试错法。
  2. 要添加一个属性(发带),我会查看当该属性存在时哪些特征地图是活动的。

在接下来的两节中,我将介绍这些方法的例子。

方法一:修改嘴

假设网络可以生成嘴巴张开和闭合的图像(这是欺骗鉴别者所必需的),并且每一层的特征图都是最终图像的表示,那么修改特征图可以用于张开或闭合嘴巴是有意义的。但是,不能保证修改单个要素地图会导致有意义的变化-我们可能需要修改许多要素地图的组合才能获得所需的结果。也就是说,单一要素地图更容易使用(至少在当前工具下),因此查看每个要素地图如何影响图像可以作为一个起点。

以下是我如何找到特征图 33 来张开/闭上嘴巴。首先,我用“添加标签”按钮添加了一个 16x16 的标签(如下)。我选择这个分辨率是因为它能产生合理的嘴巴大小的盒子。较小的分辨率将改变嘴以外的区域,而较大的分辨率通常导致比张开或闭合嘴更精细的变化(此时分辨率的选择是启发式的)。通过再次单击“查看所有 Fmap Mults ”,我们看到没有为新选项卡设置任何功能图。然后我将滑块移动到 190 左右,这也是一个基于过去模型经验的启发式决定。最后,和我们之前做的一样,我选择了包含嘴的两个盒子。

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

Adding a new tab, which initially does not influence any feature maps

然后,我选择批处理- > Fmap - >轴对齐,并选择 512 张图像进行生成。

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

这实际上会产生 1024 个图像,因为对于每个特征图,它会将乘数栏中指定的值(在本例中为 190)添加到图像中标记的空间位置(嘴)。批量生成会弹出一个窗口,显示已经生成的图像数量,并允许您中断该过程。点击“生成图像”旁边的计数器,打开它们被写入的目录:

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

The number prepended to the file names is the feature map that was modified

以‘33 _ n _ sample’(n 代表负)开头的样本明显有开口,而‘33 _ p _ sample’没有。这意味着当从嘴周围的特征图 33 中减去 190 时,嘴张开了。

我使用“设置 Fmap”框(如下)将特征映射 33 设置为-1。这使得向右移动滑块将打开嘴巴(这感觉比将特征地图 33 设置为 1 并使标签名为“mouth_close”)更直观,我使用“重命名标签”按钮将标签重命名为 mouth_open。重命名选项卡旁边的保存选项卡按钮可用于保存选项卡。

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

方法二:加一个发带

这种方法依赖于找到具有所需属性的现有图像。这需要一个图像库来处理,可以用 Batch- > New latents 生成。在这种情况下,我通常会将质量条移过中间一点,以确保有合理的变化量。

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

可能需要几百个样本才能得到几个带发带的样本。我在 tutorial_files 目录中添加了一个,我将在本教程中加载它(sample_02.png)。加载之前,创建一个 16x16 的选项卡,并确保“Fmaps To Get”设置为“All”或“Current Tab”(如下)。创建新图像时,这些选项从网络获取并存储额外的输出:当前选项卡或所有选项卡的特征映射。这可能会减慢速度,所以它不是默认选项(而且在撰写本文时,它有 0.5%的机会导致崩溃,与大批量相关)。

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

然后,执行以下操作:

  1. 选择发带周围的框
  2. 在“添加视图特征映射按钮”下,选择“按选择的相似性排序”
  3. 按“更新此图像”

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

这将向“功能图输出”选项卡添加一组按钮:

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

这些按钮对应于特征地图。它们按特征图的点积大小和图像上的选择(展平后)进行排序。这使得发带周围具有较大幅度的特征贴图会在按钮列表中较早显示。

这是第 310 张特征图的一个例子。蓝色对应正值,红色对应负值。某个位置的绝对值越大,在该位置绘制的方框就越厚、越饱和。

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

该特征图似乎在发带周围以及嘴周围具有大的正值。虽然它显然不仅仅用于发带,但我们可以尝试修改它,看看结果是什么。将该选项卡的特征映射 310 设置为 1,擦除发带周围的选择(ctrl+单击并拖动),并再次加载 sample_01.png。

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

尝试选择头发,将权重增加到 100 左右,并更新图像:

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

……没什么变化。然而,由于我们没有产生任何奇怪的人造物,将星等增加到 100 以上可能不会有什么坏处。

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

大约 600 年,我们得到了看起来像一个发带。我最初的假设是,我们需要使用大星等的一个原因是因为发带不常见。

对于包含在工具中的发带标签,我设置了几个其他的特征贴图,在示例图像中,这些贴图在发带周围是活跃的,设置为 3。将它们设置为大于 1 的数字有助于规范化选项卡的预期范围,因此将乘数设置为 100 左右应该可以表达所需的属性。

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

3.2 自动特征检测

我们修改属性的方法的一个问题是,它需要手动选择我们想要改变的方块。与在‘嘴部张开’方向上修改潜在向量相比,这无疑是不利的,因为在‘嘴部张开’方向上修改潜在向量不需要我们知道嘴部的位置(即使它也修改非嘴部特征)。这使得该方法无法很好地扩展;虽然比绘图容易,但每次修改仍然需要人工干预。然而,正如我们在上一节中看到的,一些特征地图与属性的位置相关联:例如,特征地图 310 可能被用于一般地检测发带。让我们看看能否找到一种方法,仅使用特征图的线性组合来检测图像中的嘴。

首先,重复用于激活发带周围的特征贴图的过程,只是这次选择嘴:

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

像以前一样,我们可以点击一个按钮来显示一个特征图:

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

但是,在查看和比较大量要素地图时,这种方法有点慢。相反,在“从输出添加”按钮下方的文本框中键入 20,然后按按钮。然后,按“查看多个 Fmaps”。

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

带有 20 个按钮的“从输出添加”将前 20 个按钮的特征地图添加到“fmap”文本框,“查看多个 fmap”并排显示它们。

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

对应于特征图 234、34、370 和 498 的前 4 个(以及其他几个)看起来都像是嘴巴检测器。然而,我们不知道他们是否会持续检测新图像的嘴部。为了测试这一点,我们可以生成几个新的图像,质量条位于中心右侧,以获得适当的方差。首先确保“记录要素地图”已选中。使用“重置历史记录”清除现有记录。此外,确保“Fmaps To Get”设置为“Current Tab”(该工具不会记录所有分辨率的历史记录,只会记录与当前选项卡相对应的分辨率)。然后,我们可以通过使用批- >新潜在客户生成许多新图像,该工具将记录它们的特征图。

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

在这种情况下,我将生成 10 个新的图像,这将是不同于你的任何一个。要查看所有图像的相同特征图,请在 Fmaps 框中输入特征图,然后选择“查看历史”。我将对 234、34 和 370 中的每一个都这样做。

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

234:

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

34:

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

370:

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

嘴的位置变化不大也无妨,但这些特征地图似乎确实能可靠地跟踪它。

相同的过程可以应用于其他层的其他属性。以下是一些例子:

眼睛:

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

脸颊:

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

背景:

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

在许多情况下,我通过组合多个特征地图来获得最一致的结果。为此,我让 python 脚本可以从工具中调用(这对未来的特性也很有用),因为我更愿意用 numpy 进行多维数据处理。该工具使用 PATH 环境变量来查找 pythonw.exe,因此需要在运行该工具之前进行设置。脚本功能是该工具的最新特性,甚至比其他功能开发得更少。这里有一个例子:

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

spatial_map.py 存储在“脚本”目录中,您需要安装其依赖项才能使用它。该工具将路径传递到写入“脚本要素地图”中指定的要素地图的目录。然后,它组合这些特征地图并输出结果,该结果由工具读取并用于在图像中进行选择。这里有一个例子,使用了我们之前发现的一些与嘴部位置相关的特征地图:

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

移动嘴部滑块到~100 并更新图像,像平常一样张开嘴。

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

这让我们可以自动创建具有特定属性的图像。

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

通过选择“生成时运行脚本”下的“运行并修改”,将 mouth_open 设置为 100 左右,并生成新的 latents,我们可以确保新图像有一个张开的嘴巴。通过将 mouth_open 设置为-100,并将该设置应用于包含张嘴图像的目录,我们可以生成同样的闭嘴图像。

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

将设置应用到目录可以通过杂项- >将当前滑块应用到目录并选择带有生成图像的目录来完成:

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

或者,这可以使用批处理- > Fmap- >新的潜在组合一次完成:

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

请注意,该选项将基于所有乘数为非零的选项卡进行组合修改。例如,如果“头发”选项卡有一个非零乘数,我们选择生成 3 个“头发”图像和 2 个“嘴巴张开”,它将创建每个图像的 6 个变体,具有以下属性:

  • 短发,闭着嘴
  • 短发,张着嘴
  • 中等长度的头发,闭着嘴
  • 中等长度的头发,嘴巴张开
  • 长头发,闭着嘴
  • 长发,张嘴

关于脚本还有几点:

  • 您可以在要素地图前添加一个乘数,以更改各种地图组合时的影响。

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

  • 用于检测面部特征的特征图的分辨率不需要与我们输入数据的分辨率相匹配。脚本应该根据需要调整大小:

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

  • 脚本还有其他潜在用途,例如记录要素地图的表示,以便在工具关闭后进行分析。

这涵盖了该工具的大部分功能。在下一节中,我将更详细地讨论这个架构。

3.3 工具详情

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

From TensorFlow python implementation to generating images with the tool

该工具有 4 个主要组件:

  1. TensorflowInterface.dll:使用 tensorflow C api 加载模型
  2. GanTools.dll:将 TensorflowInterface.dll 加载到 C#环境中
  3. GanStudio.exe:加载 GanTools.dll 以便与模型交互
  4. 数据文件:graph.pb(模型)和 data _[graph . Pb 的 MD5 中的文件,这些文件包含特定于模型的信息并被加载以帮助交互。

这些都可以在 zip 文件中看到:

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

图像被写入“肖像”目录,标记为收藏夹的图像被写入“收藏夹”目录,而“脚本”目录包含该工具可以加载的 python 脚本。其余的文件是工具的其他组件使用的库。

数据目录包含几个文件:

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

  • saved_fmaps 包含工具启动时加载的选项卡(嘴巴张开、发带、腮红等)数据。选项卡数据包括修改了哪些要素地图、脚本信息和乘数。
  • attributes.csv 包含“Attributes_0”选项卡用来以有意义的方式修改潜在向量的向量。
  • average_latent.csv 包含通过对映射网络生成的大约 10,000 个潜在值进行平均而生成的平均(中间)潜在值。质量/唯一性栏使用它来应用截断技巧。
  • latents.csv 包括所有生成图像的潜在值。在我开始将潜在值附加到图像之前,它更重要,但它仍然可以作为备份。

所有这些都是特定于模型的,这就是为什么我通过将 graph.pb 文件的散列附加到目录名来将目录绑定到模型的原因。

TensorflowInterface.dll 是加载 graph.pb 的组件,它使用tensorflow.dll与之交互。它通过导出 3 个主要函数来实现这一点:

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

这种交互的大部分是使用模型中张量的名称来完成的。虽然源代码是可用的,但我认为获得这些名称(以及更好地理解模型)的最好方法之一是通过像 netron 这样的工具。在下图中,netron 用于检查添加操作的输入,该操作合并了此工具所做的要素地图修改。

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

Operations to allow adding/subtracting values in feature maps (screenshot of Netron)

4.结论

结合本系列的前一篇博客,我已经写了一份相当全面的记录,记录了到目前为止我为这个项目所做的工作。这些博客没有详细介绍的两个例外是生成 512x256(高 x 宽)图像的能力和我用更多男性图像训练的模型。

本博客中使用的模型实际上可以产生 512x256 的图像,但是我在网络早期就切掉了底部:

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

Using netron to view graph.pb

这提高了图像创建速度,减小了 UI 的大小,但最重要的是,它保持了图像适合所有观众。

我最初认为有男性图像的模型更糟糕,因为图像的方差增加了,平均质量降低了,为了包含大量的男性图像,我必须降低“最喜欢的”阈值(见原始博客)。然而,当我更新我的 StyleGAN 重新实现的代码时,我注意到我在实现样式混合时引入了一个 bug ( 这里是修正)。该错误意味着用于从中间潜在到风格转换的权重在两个自适应实例规范化层之间共享。这降低了网络的容量,也可能是新模型比旧模型更差的原因。这是需要处理的最令人讨厌的错误类型之一:它不会阻止模型工作,但它会以一种直到模型完成训练才真正注意到的方式降低性能,并且性能的降低可能归因于其他因素。安德烈·卡帕西在这一系列推文中说得很好:

对于个人项目,包括这篇博客中讨论的工具,我更喜欢使用“快速修复”的方法(恶意软件分析让我几乎享受到了调试的乐趣)。然而,这对于实现深度学习模型不起作用,尽管我在使用 TensorFlow 时倾向于更慢地移动以避免这样的错误,但有时如果我在项目的不同部分花费了太多时间,它们仍然会出现。这是我正在积极尝试改进的地方,我发现一个有用的策略是在 TensorBoard 或 netron 中查看模型的图形,以获得不同的视角。

由于这个工具主要是基于查看和修改特征地图,我有兴趣使它适用于除了 GANs 之外的生成模型。如果可以将任意图像作为输入的模型可以像 StyleGAN 一样在其内部表示中演示相同类型的无监督面部特征检测,那将是一件好事。我还想将这种动画制作方法与视频到视频合成进行比较,并对使用生成模型制作动画的其他工作进行更多研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值