TowardsDataScience 博客中文翻译 2019(二百零七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

HTML 画布的乐趣:让我们制作熔岩灯等离子体

原文:https://towardsdatascience.com/fun-with-html-canvas-lets-make-lava-lamp-plasma-e4b0d89fe778?source=collection_archive---------11-----------------------

Real time lava-lamp style plasma

在这篇文章中,你将学习如何创建上面嵌入的熔岩灯等离子体效果。该效果是实时计算的,并使用 HTML 画布进行渲染。

如果你在手机上,看不到嵌入,你可以直接打开嵌入页面

不需要高等数学。我会解释所有我们需要的数学概念。我们将使用sincos函数,这就是这个效果所需的所有数学复杂性。

如果您想查看完成的代码,请在另一个窗口中打开源代码

HTML

我们的 HTML 结构是基本的。我们创建一个主体,在其中放置一个画布,并对我们的标记进行样式化,使我们的画布总是填充整个窗口。

<!DOCTYPE html>
<body
  style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; overflow: hidden; margin: 0; padding: 0;"> <canvas 
    id="canvas"
    style="width: 100%; height: 100%; padding: 0;margin: 0;"
  ></canvas></body>

设置

我们将画布设置为固定大小的512 x 512像素。画布的像素被设计为拉伸填充整个窗口,但是内部像素缓冲区的大小是固定的512 x 512

const canvas = document.getElementById("canvas");
const c = canvas.getContext("2d");*// size of canvas* const imgSize = 512;canvas.width = imgSize;
canvas.height = imgSize;

我们不打算在画布上使用任何绘图功能,我们将把它当作一个像素缓冲区。我们不能直接操作画布上的像素值,但我们可以创建一个与画布大小相同的图像缓冲区,根据需要操作图像缓冲区,然后将其内容绘制到画布上。

createImageData 的调用为我们提供了一个与画布大小相同的图像缓冲区。

*// init image data with black pixels*const image = c.createImageData(imgSize, imgSize);for (let i = 0; i < image.data.length; i += 4) {
  image.data[i] = 0; *// R* image.data[i + 1] = 0; *// G* image.data[i + 2] = 0; *// B* image.data[i + 3] = 255; *// A* }

返回的图像数据是像素数据的一维数组。每个像素由四个连续的值表示。这四个值是 0-255 范围内的整数强度。它们依次保存像素的红色、绿色、蓝色和 alpha 分量的值。

该阵列按行存储所有图像像素。第一行的像素先出现,然后是第二行,依此类推,行与行之间没有间隙。

我们将所有的图像像素初始化为不透明的黑色。

效果总结

该效果依赖于构造两个高度图,在相对运动时组合它们,并根据高度值的总和对输出进行着色。

1 号高度图

我们想填充第一个1024 x 1024高度图,平滑值范围从 0…128.

我们希望值的分布平滑,没有噪声。我们将在第二个高度图中添加噪声。第一个高度图的工作是给我们一些各个方向的平滑的波浪作为可预测的基础层。

我们希望生成一个高度图,当在透视图中渲染时,看起来像这样:

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

height map 1

我在用正弦函数生成高度图。还记得窦的功能吗?以下是相关事实:

  • 它产生连续的波浪
  • 它是周期性的,频率为 2π
  • 它生成-1 和 1 之间的值

我们来看看。

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

plain sin(x)

我们想要产生一个大约有 1.5 个完整振荡的波。这就是我们从 sin 函数中得到的,当我们给它 0 到 3π的值时。

如果我们适当的缩放比例,我们可以得到任何我们需要的频率。例如,如果我们想要在 x = 0 和 x = 512 之间产生 1.5 次振荡,我们不会将x传递给 sin,但是x × 3π/512``x越接近 512,我们的转换输入值就越接近 3π。

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

graph of sin(x × 3π/512)

接下来我们需要关注的是输出范围。sin 函数给出了介于-1 和 1 之间的值。我们想要 0…128.

我们可以转换-1…1 到我们想要的范围,首先归一化到 0…1.为此,我们加 1,然后除以 2。值在 0 之间…1 我们可以到达 0…乘以 128 得到 128。

因此,我们完成的发电机公式是:

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

该图具有我们需要的所有属性:

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

we have a wave in range (sin(x×3π/512)+1)/2*128

我们有一个很好的函数来生成我们的高度图。但是我们如何用合适的值填充 2D 高度图呢?

我们确定每个像素到高度图中心的距离,并使用距离作为高度生成函数的输入。相同的距离像素产生相同的高度值,所以我们得到一个从中心出现的径向波值。

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

Same distance pixels generate the same height value

为了计算像素到中心的距离,我们首先将像素坐标偏移地图宽度和高度的一半。这有效地将坐标移动到以原点为中心的坐标系。

像素坐标允许我们构建一个直角三角形,所以我们可以使用勾股定理告诉我们distance² = cx² + cy²

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

the distance from origin is given by sqrt(cx²+cy²)

第一张高度图就这样了。对应的代码是:

二号高度图

我们想用 0 范围内的值填充第二个1024 x 1024高度图…127.这一次我们想要不规则。它们需要在空间上连贯一致,从山丘到山谷平滑过渡。

由于我们已经熟悉了 sin 函数,我将继续使用 trig 函数来生成第二个高度图。在透视图中,它看起来像这样:

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

Height map 2

这张地图不是随机的。事实上,这是非常有规律的。它甚至关于 x 轴和 y 轴都是对称的。它只是混杂在一起,足以有一个有机的感觉,这就是我们所要寻找的。

它由正弦和余弦函数之和构成,这些函数在偏离中心的轴伪距离上运算。像以前一样,输入被缩小以产生适合我们的地图大小的波浪量,输出被缩放为 0…127.

构建高度函数

主要思想是使用正弦和余弦之和来产生波状不规则性。出于我们的目的——这里纯粹是审美目的——我们将 cos 函数理解为 sin 函数的相移版本。

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

cos is just a phase shifted version of sin

这次我们不想要径向对称,所以我们不打算用距离作为这些函数的输入。相反,我们将使用轴倾斜版本的距离。

对于 sin 部分,我们将拉伸 y 坐标,收缩 x 坐标。然后,我们缩小输入,以达到一个适合我们地图大小的好看的频率。

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

对于 cos 部分,我们遵循同样的思路,但是沿 x 轴拉伸,沿 y 轴收缩。

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

这两个部分为我们产生了规则的椭圆波形。当我们把它们加起来,它们就形成了我们想要的不规则的山丘景观

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

sin, cos, and combined waves

我们仍然需要将输出标准化到 0…127.当我们将 sin 和 cos 的输出相加时,我们得到-2 范围内的值…2,所以我们加 2,除以 4,得到值 0…1,然后缩放 127 以得到我们想要的范围。

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

第二张高度图到此为止。下面是相应的代码:

渲染高度贴图

我们将我们的高度图构造为1024 x 1024,与我们的可见图像尺寸512 x 512相比,它在每个方向上的尺寸都是两倍

为了使高度图可见,我们可以迭代任何512 x 512部分,从高度值中导出每个像素的显示颜色,并将颜色放入我们的图像缓冲区。

因为高度图包含 0…128 和 0…127,我们可以将它们相加,得出范围为 0…255.最简单的开始是渲染相应的灰度值,其中红色、绿色和蓝色分量是相同的。

如果我们对高度图 1、高度图 2 以及它们的值的总和(都从左上角开始)执行此操作,我们将得到以下图像:

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

height map 1, height map 2, and the sum of their values

下面是更新图像像素缓冲区的相应代码:

高度场动画

我们应该开始制作我们的高度图来获得初步的视觉效果。我们应得的。

这个想法是动画的高度地图的位置相对于彼此。请记住,我们有1024 x 1024高度图,但只有一个512 x 512图像视图,因此在每个高度图上,对图像有贡献的第一个——左上角——像素可以是范围[0..512]x[0..512]内的任何像素

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

different offsets of height maps create different values in the view port

我们将使用requestAnimationFrameAPI 来启动我们的动画。当需要渲染下一帧时,它会调用我们的tick函数。浏览器会尽力给我们一点时间来绘制下一帧,通常目标是每秒 60 帧。我们需要请求在我们完成后再次被呼叫。

在我们的 tick 函数中,我们将高度图偏移更新到新的位置,重新计算新的图像数据,并将图像呈现到画布上。

随时间插值高度图偏移

当我们的回调函数被调用时,浏览器会传入一个以毫秒为单位表示时间的高分辨率数值。每次调用我们的时候,这个参数都会比上次调用的毫秒数高。我们可以将这一事实用于基于时间的动画,从而独立于帧速率。

我们将使用时钟值来驱动高度贴图偏移的动画。我们需要在两个高度图上的两个轴上插入 0 到 512 之间的值。我们将为四个偏移值中的每一个单独做这件事,因为我们不希望运动开始看起来太有规律或重复。

我们的目标是移动高度贴图的中点,使其在视口中漫游,如下所示:

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

desired middle point movement of our height maps within the viewport

是的,既然我们已经对 sin 和 cos 有了充分的了解,我们就用它们来插值时间偏移。

给定一个进行中的时间值t,我们的插值必须给我们一个范围为 0 的值…512.

我们在构建高度图时使用的相同原则在这里也适用。我们将缩小t来延长一个完整振荡发生的时间。然后,我们通过加 1,除以 2,然后乘以 512 来缩放到所需的范围。

我们还想增加一些变化,因为我们想对四个偏移量进行稍微不同的插值。为此,我们可以改变时间比例因子,并给t增加一个常数,以在输出中产生相移。

这是我们的基本公式:

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

对于四个偏移中的每一个,我们将对timeScalephaseShift使用稍微不同的值,以避免锁步重复动画。下面是相应的代码:

随着偏移动画的到位,我们已经得到了等离子体效果的灰度版本。

我们迄今为止的进展

这是我们迄今为止取得的进展:动画灰度等离子体

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

screenshot of grey-scale plasma sandbox

添加颜色

我们将通过添加颜色来改善我们的等离子效果。首先,我们将添加静态颜色,然后我们将提出一个随着时间改变颜色的策略。

使用单一调色板

主要思想是使用一组固定的 256 种颜色来对应范围 0…255.

如果我们认为低值是谷,高值是山,我们可以决定将山设为蓝色rgb 0,0,255而将谷设为红色rgb 255,0,0,并在颜色之间进行线性插值。我们可以预先计算所有 256 种颜色,并将它们放入一个数组中,这样高度值为 0…255 索引成与高度相对应的颜色。

如果我们用r, g, and b键将颜色表示为对象,我们可以使用辅助函数在两种颜色之间进行插值:

// c1 and c2 are colors
// f is a fraction between 0..1
//
// returns an interpolated color between 
//   c1 (for f=0) and
//   c2 (for f=1)
//
// pass f=0.5 to get the color half-way between c1 and c2const interpolate = (c1, c2, f) => {

  return {
    r: Math.floor(c1.r + (c2.r - c1.r) * f),
    g: Math.floor(c1.g + (c2.g - c1.g) * f),
    b: Math.floor(c1.b + (c2.b - c1.b) * f)
  };
};

使用双色渐变

我们可以生成 256 种颜色的渐变,如下所示:

// generates an array of 256 colors 
// forming a linear gradient of the form
// [c1, ..., c2]const linearGradient = (c1, c2) => {
  const g = [];
  *// interpolate between the colors in the gradient* for (let i = 0; i < 256; i++) {
    const f = i / 255;
    g[i] = interpolate(c1, c2, f);
  }
  return g;
};

有了这些助手,我们可以像这样定义一个调色板:

let palette = linearGradient(
   { r: 255, g: 255, b: 0 },  // c1 yellow
   { r: 0, g: 54, b: 128 }    // c2 dark blue
);

updateImageData中,我们可以使用调色板中对应于高度值的颜色:

*// height value of 0..255* let h = heightMap1[i] + heightMap2[k];*// get color value from current palette* let c = palette[h];

以下是使用两种颜色之间的线性渐变的一些结果:

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

Using a linear gradient between two colors

整体视觉效果还是和灰度着色差不多。我们可以通过在渐变中添加额外的不同颜色来创造更多样的外观。

五色渐变

让我们创建一个渐变函数,它接受五种不同的颜色,并在它们之间进行插值。

创建五种颜色的调色板极大地增强了我们输出的多样性。

通过在中等高度的部分选择任意的颜色,我们将我们对深度的感知与潜在的高度值分离。

我们可以自由地为调色板的中间选择浅色,为相邻的颜色选择深色,以获得更高和更低的值。在我们的感知中,这使得高度图的中间色调看起来像脊线。这种效果是我们只有用两种以上颜色的渐变才能达到的。

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

multi-color palettes can effectively accentuate or blur areas in our height map

使用两种调色板

现在我们可以轻松地创建调色板,我们可以创建两个调色板,并随着时间的推移在它们之间进行插值。

我们想开始创造性地使用颜色,所以在我们继续之前,让我们快速添加一个助手来创建一个随机的 5 色渐变调色板。

*// returns a random color* const randomColor = () => {
  const r = Math.floor(Math.random() * 255);
  const g = Math.floor(Math.random() * 255);
  const b = Math.floor(Math.random() * 255);

  return { r, g, b };
};// returns a random 5-color gradient palette
const makeRandomPalette = () => {
  const c1 = randomColor();
  const c2 = randomColor();
  const c3 = randomColor();
  const c4 = randomColor();
  const c5 = randomColor();

  return makeFiveColorGradient(c1, c2, c3, c4, c5);
};

每一帧我们计算一个范围为 0 的值…1,并将当前帧的调色板创建为源调色板的相应颜色之间的插值。

我们将使用 cos 函数进行插值。应用与之前相同的规则:我们拉伸t值来控制振荡时间,然后归一化为 0…通过加 1 除以 2 得到 1。

*// two palettes we interpolate between* const palettes = [makeRandomPalette(), makeRandomPalette()];*// current palette is edstablished durting animation* let palette = [];const updatePalette = t => {
  const timeScale = 0.0005;
  const x = t * timeScale;

  *// normalized value 0..1 used to interpolate palette colors* const inter = (Math.cos(x) + 1) / 2; *// create interpolated palette for current frame* for (let i = 0; i < 256; i++) {
    palette[i] = interpolate(palettes[0][i], palettes[1][i], inter);
  }
};

现在,我们可以使用两个调色板来实现我们的效果不同的艺术意境。

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

Given two color palettes we can interpolate between the two each frame

转向时生成新调色板

两个调色板之间的混合很好,但仍然有些静态。最后,随着时间的推移,我们将生成新的调色板。

当在两个调色板之间来回切换时,最好能知道我们何时到达插值的远端,也就是说inter何时分别到达01,并且即将开始向相反的方向移动。

一旦我们检测到这个事实,我们可以在另一端生成一个新的调色板,并平滑地插入一组新的颜色。

仅仅检查inter的具体值是不够的。首先,我们不能指望准确地击中01

假设我们在inter = 0.01,直到下一帧足够长的时间过去,使得 cos 函数经过0并再次移动到0.01。这种情况也说明了我们的下一个问题:仅仅看插值并不能告诉我们前进的方向。我们应该重新生成两个调色板中的哪一个?

检测转向点

我们需要插值函数cos导数。我们需要看看-sin

cos的导数给出了cos任意给定点处切线的斜率。实际上:当一个函数在点x向下时,导数函数在点x为负。当函数在点x向上时,导数在点x为正。

因此,我们可以寻找导数符号的变化。如果前一帧的导数是负的,而当前帧的导数是正的,我们已经改变了方向,正在向第二个调色板插值。

同样的想法适用于相反的情况。如果前一帧的导数是正的,而当前帧的导数是负的,我们已经改变了方向,正在向第一个调色板插值。

cos的导数是-sin。通过查看图表,说服自己上述陈述是正确的。

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

cos(x) and its derivative -sin(x)

cos向下时,-sin为负。当cos抬头时,-sin为正。在cos改变方向的点-sin0

我们将使用这些事实,并扩展我们的调色板插值,以在检测到导数符号变化时生成新的调色板。

根据新的导数是正的还是负的,我们知道我们正在向哪个调色板插值,所以我们可以替换正确的那个。

对新生成的调色板插值结束了我们的工作。等离子效果完成了。

恭喜你走到这一步!

结论

哇,那真是一段旅程。在这里找到完整的工作源码。坐下来享受你创造的一切吧!

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

random screenshot of lava lamp plasma

PS: 看看这篇用 HTML 创建星球大战标题抓取的文章。这比血浆更容易做到,但也相当值得。它看起来非常接近真实的交易。

[## 创建一个 HTML 格式的星球大战标题抓取

很久很久以前,在一个遥远的浏览器里

medium.com](https://medium.com/better-programming/create-a-star-wars-title-crawl-in-html-c25a76fea401)

StyleGAN 的乐趣:让我们预测特斯拉 CyberTruck 的设计!

原文:https://towardsdatascience.com/fun-with-stylegan-lets-predict-the-tesla-cybertruck-design-3f2123ab4d0d?source=collection_archive---------35-----------------------

当你从一辆旧的皮卡和一辆特斯拉 Model X 的潜在组合中生成一辆带有 StyleGAN 的汽车时,会发生什么?

11 月 21 日,埃隆·马斯克将推出一款新的特斯拉皮卡,他说

所以自然地,作为一名数据科学家和特斯拉粉丝,我必须探索人工智能是否可以预测这辆卡车的设计!

经过数周的尝试和失败,我终于找到了一个名为 StyleGAN 的生成式人工智能模型,它可以产生想象中的汽车设计,我用这个模型结合了一辆皮卡和一辆特斯拉 Model X 的设计,给了我们一个特斯拉卡车的估算设计。

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

StyleGAN output for Old Pickup Truck + Tesla Model X

在进一步将假想的特斯拉卡车的设计与一辆装甲运兵车的设计相结合后,我们根据埃隆的描述得到了赛博卡车设计的最佳估计。

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

Is this what the Tesla CyberTruck will look like? StyleGAN seems to think so.

理解风格 g

今年早些时候,NVIDIA 的研究人员推出了 StyleGAN 。它最出名的是能够生成看起来非常真实的随机人脸。他们还提供生成汽车的预训练模型,这是我在这个项目中使用的。

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

These people are not real. These are random faces generated by StyleGAN. [source]

任何 GAN 方法的典型发生器网络使用从高斯分布提取的随机噪声像素的图像作为起点。然后,它学习将这个有噪声的图像转换为目标分布,在我们的例子中是一辆汽车的图像。

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

生成器使用几个卷积层来学习这个目标分布,使用数千个图像样本用于其训练过程。然而,StyleGAN 的工作方式略有不同。它在输入噪声层和卷积层之间引入了一个额外的映射网络。映射网络仅由完全连接的层组成,这些层接收噪声图像并将其转换为一些有意义的潜在空间表示。

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

Mapping Network in StyleGAN

这个中间的潜在空间包含多个层,其中每一层都表示一个特定的造型元素,生成器在试图生成汽车图像时需要使用该元素。因此,一层可能包含关于汽车颜色的信息,另一层可能包含关于位置和角度的信息,如前视图或后视图。一层可以包含前灯设计的信息等等。

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

The Generator/Synthesis Network of StyleGAN.

现在是斯蒂根模型的生成器。该模型使用小尺度的恒定图像作为在训练过程中学习的起点。几个上采样和卷积层处理这个输入。来自映射网络的中间潜在表示在这里被用于不同的级别,以控制输出汽车图像的设计。这就是 StyleGAN 能够生成随机汽车图像的方式。

然而,这里有一个问题。

虽然我们可以使用 StyleGAN 来转换随机噪声输入,使其看起来像汽车,但我们无法控制汽车的设计元素。换句话说,对于任何给定的输入,我们无法控制映射网络的输出。因此,如果我们想为特斯拉 CyberTruck 生成具有特定风格特征的汽车,我们将需要以某种方式修改这种潜在的风格,以符合 CyberTruck 的描述。只有这样,我们才能使用合成网络来可视化汽车的最终输出图像。

编码器拯救世界。

谢天谢地,有人为这个问题找到了一个巧妙的解决办法。名为 StyleGAN-Encoder 的 github repo 将在这里帮助我们。

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

使用这个编码器,我们可以训练另一个神经网络,它充当一个**“反向生成器”**。它可以将任何汽车的图像(如特斯拉 Model X)作为其输入,并产生相应的潜在空间作为其输出。一旦潜在空间可用,我们可以对其进行修改,并根据我们的意愿改变输入车的设计。很聪明,对吧?

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

所以,这里是这个反向发电机的工作原理。首先,我们使用一个预训练的 ResNet 模型来转换我们想要改变其设计的汽车的图像,并将其转换为潜在的风格作为其输出。为此生成训练数据非常容易,我们可以简单地使用 StyleGAN 生成器来生成几乎无限的输入-输出样本对。使用这个,我们可以得到一个由 StyleGAN 的映射网络产生的潜在空间的相当不错的估计。

但是,仍然有一些更多的优化需要做。一旦我们从 ResNet 获得了对潜在风格的粗略估计,我们就可以用它来生成汽车的相应图像。损失函数现在可以由 VGG 感知损失 度量来定义,该度量测量两个输出图像在视觉上看起来有多不同。对于比较图像之间的视觉相似性来说,这是一个比诸如均方误差之类的更健壮的度量。

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

现在,为了优化潜在的风格,我们需要做的就是通过这个组合网络反向传播,同时保持所有网络权重固定。这里唯一允许训练的参数是输入风格潜伏。因此,经过几次反复训练后,你可以很快收敛到潜在风格的最佳估计值。

玩潜伏空间!

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

我们现在准备通过组合不同汽车的潜在风格,挑选我们想从哪辆汽车中选择的设计元素,来玩转这种潜在风格。然后,我们可以简单地使用 StyleGAN 生成器来产生混合多种汽车设计的结果。

我必须承认,和甘斯一起工作时,玩这个潜在空间是你能得到的最大乐趣!

结果:赛博卡车预测!

我摆弄了一下这个人工智能,得到了一些关于即将推出的特斯拉 CyberTruck 设计的有趣预测。我将不同的设计风格混合到一辆旧学校皮卡的潜在空间中,得到了 StyleGAN 输出,如下图。

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

  1. 第一个简单地将卡车转换成 Model X 克隆,如果不是因为埃隆·马斯克对网络卡车的疯狂设计想法,这将是一个安全的选择。
  2. 这就是为什么我认为第二种选择看起来最接近网络卡车的实际样子。它结合了
  3. 第三种选择也可能是真的,如果埃隆决定让它看起来像一辆复古的银翼杀手汽车,但不幸的是,它看起来不再像特斯拉汽车。

我投票给最接近真实情况的选项 2。但是,你们觉得呢?你认为赛博卡车看起来会像这些预言中的任何一个吗?或者你认为它会看起来完全不同,一些我们从未见过的东西?请在下面的评论中告诉我!

参考

  1. 学习如何用一个生成性对抗网络来变形人脸!Arxiv 洞察
  2. 用 nVidia StyleGAN 和 Python (7.3)生成人脸作者杰夫·希顿
  3. StyleGAN 作者亨利·艾实验室

感谢您的阅读。如果你喜欢这篇文章,你可以在媒体GitHub 上关注我的更多作品,或者订阅我的 YouTube 频道

有趣的二项式分布

原文:https://towardsdatascience.com/fun-with-the-binomial-distribution-96a5ecabf65b?source=collection_archive---------4-----------------------

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

理解不太为人所知的正态分布,以及如何应用它

每个人都知道并喜欢正态分布。它被用于各种各样的应用,如投资建模、A/B 测试和制造过程改进(六西格玛)。

但是人们对二项分布不太熟悉。这很遗憾,因为二项分布真的很有用。有人问过你类似这样的问题吗:

“给定一枚硬币,掷 10 次,得到 6 个头的概率是多少?”

概率(尤其是餐巾纸后面的概率计算)不是我最喜欢的东西。所以当我第一次学习二项分布的时候,我想,“是的,我再也不用担心抛硬币的概率问题了!”

那是因为抛硬币的结果遵循二项分布。我应该强调一下,大数定律在这里适用。为了技术上的正确,我应该说,如果我们一遍又一遍地重复进行同一套实验(投掷硬币 10 次),我们在所有这些实验中观察到的人头数将遵循二项式分布。

不要担心,我很快会详细说明这一点。

什么是二项分布

首先让我们从稍微更技术性的定义开始— 二项式分布是一系列实验的概率分布,其中每个实验产生一个二元结果,并且每个结果都独立于所有其他结果。

一次抛硬币是二进制结果实验的一个例子。抛硬币也符合另一个二项式分布要求——每一次抛硬币的结果都是独立的。需要明确的是,实验的结果不需要像掷硬币一样具有相同的可能性,以下内容也符合二项分布的前提条件:

  • 不公平的硬币(例如有 80%的概率正面朝上)。
  • 在街上随机挑选一些人来回答是或否的问题。
  • 试图说服网站访问者购买产品——是或否的结果取决于他们是否购买。

概率和统计的新手可能会遇到的一个问题是概率分布的概念。我们倾向于确定性地思考,比如“我将一枚硬币抛 10 次,产生了 6 个头”。所以结果是 6,那么分布在哪里呢?

概率分布来源于方差。如果你和我都掷 10 枚硬币,很可能我们会得到不同的结果(你可能得到 5 个正面,而我得到 7 个)。这种方差,也就是围绕结果的不确定性,产生了一种概率分布,它基本上告诉我们哪些结果相对更有可能(比如 5 个头),哪些结果相对不太可能(比如 10 个头)。

我们可以通过模拟产生这样的概率分布,如下图所示:

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

Illustration of a Sequence of Trials that Would Produce a Binomial Distribution

在我们进入运行这个模拟并产生二项式分布的 Python 代码之前,让我们先了解一些定义。当你看到教科书中描述的二项式分布及其背后的实验时,这些描述总是包括以下关键参数:

  1. n: 我们做实验的次数。在我们的硬币例子中,n 等于 10(每个实验是硬币的 1 次翻转)。
  2. p: 成功概率。公平地说,应该是 50%。
  3. k: 成功的目标次数。之前我们提到过我们希望获得 6 项成功。

用 Python 实现二项式分布

让我们来看一些运行上述模拟的 python 代码。下面的代码 ( 也可以在我的 Github 这里得到 ) 做了如下的事情:

  1. 生成一个介于 0 和 1 之间的随机数。如果这个数字是 0.5 或者更多,那么把它算作正面,否则就是反面。使用 Python list comprehension 做 n 次。这在函数 run_binom 中通过变量 tosses 实现。
  2. 重复指定次数(试验次数由输入变量试验指定)。我们将进行 1000 次试验。
# Import libraries
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# Input variables# Number of trials
trials = 1000# Number of independent experiments in each trial
n = 10# Probability of success for each experiment
p = 0.5# Function that runs our coin toss trials
# heads is a list of the number of successes from each trial of n experiments
def run_binom(trials, n, p):
    heads = []
    for i in range(trials):
        tosses = [np.random.random() for i in range(n)]
        heads.append(len([i for i in tosses if i>=0.50]))
    return heads# Run the function
heads = run_binom(trials, n, p)# Plot the results as a histogram
fig, ax = plt.subplots(figsize=(14,7))
ax = sns.distplot(heads, bins=11, label='simulation results')ax.set_xlabel("Number of Heads",fontsize=16)
ax.set_ylabel("Frequency",fontsize=16)

这就是代码。那么,当我们重复 10 次掷硬币试验 1000 次时,会发生什么呢?我们得到下面绘制的直方图:

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

Our Simulation Results

让我们修改前面代码的绘图部分,以便我们的绘图也显示实际的二项式分布(使用 scipy 库中的 stats.binom 函数):

# Plot the actual binomial distribution as a sanity check
from scipy.stats import binom
x = range(0,11)
ax.plot(x, binom.pmf(x, n, p), 'ro', label='actual binomial distribution')
ax.vlines(x, 0, binom.pmf(x, n, p), colors='r', lw=5, alpha=0.5)
plt.legend()
plt.show()

下图用蓝色显示了我们最初的模拟分布,用红色显示了实际的二项式分布。要点是,如果我们实际上重复 10 次抛硬币 1000 次,二项式分布是我们观察到的非常好的近似值——因此,我们可以直接使用二项式分布,而不是浪费大量时间来抛硬币和记录结果!

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

Actual Binomial Distribution (Red) vs. Our Simulation Results (Blue)

如果我们想模拟一系列 n 次实验的结果(回想一下,在我们的例子中 n=10,但实际上它可以是任何正整数),我们可以使用二项分布的随机变量来生成结果,如下所示:

np.random.binomial(n, p)

最后,让我们通过对 10 次抛硬币进行 10,000 次模拟来回答我们最初的问题(10 次抛硬币得到 6 个正面的概率):

# Probability of getting 6 headsruns = 10000
prob_6 = sum([1 for i in np.random.binomial(n, p, size=runs) if i==6])/runs
print('The probability of 6 heads is: ' + str(prob_6))

我们发现概率约为 20%(我们也可以通过 x 轴上红色垂直线高于 6 的高度在之前的直方图中看到这一点)。

二项分布在现实世界中的应用

酷,但是如果我们想分析抛硬币以外的事情呢?让我们来看一下二项分布的一个程式化的真实世界用例。假设我们是数据科学家,负责提高我们公司呼叫中心的 ROI(投资回报),在这里,员工试图给潜在客户打电话,让他们购买我们的产品。

您查看了一些历史数据,发现了以下情况:

  • 典型的呼叫中心员工平均每天打 50 个电话。
  • 每次通话的转换(购买)概率为 4%。
  • 你公司每次转换的平均收入是 20 美元。
  • 您正在分析的呼叫中心有 100 名员工。
  • 每个雇员每天的工资是 200 美元。

我们可以将每个员工视为具有以下参数的二项分布随机变量:

n = 50

p = 4%

以下代码模拟了我们的呼叫中心:

# Call Center Simulation# Number of employees to simulate
employees = 100# Cost per employee
wage = 200# Number of independent calls per employee
n = 50# Probability of success for each call
p = 0.04# Revenue per call
revenue = 100# Binomial random variables of call center employees
conversions = np.random.binomial(n, p, size=employees)# Print some key metrics of our call center
print('Average Conversions per Employee: ' + str(round(np.mean(conversions), 2)))
print('Standard Deviation of Conversions per Employee: ' + str(round(np.std(conversions), 2)))
print('Total Conversions: ' + str(np.sum(conversions)))
print('Total Revenues: ' + str(np.sum(conversions)*revenue))
print('Total Expense: ' + str(employees*wage))
print('Total Profits: ' + str(np.sum(conversions)*revenue - employees*wage))

如果您运行该代码,您会得到如下输出(每次运行都会发生变化,因为转换是一个二项分布的随机变量数组)呼叫中心的关键指标:

  • 每位员工的平均转换率: 2.13
  • 每个员工转换率的标准偏差: 1.48
  • 总转换数: 213
  • 总收入:21300 美元
  • **总费用:**20,000 美元
  • 总利润:【1,300 美元

与费用相比,利润相当微薄。但这些只是随机产生的一天的结果。让我们看看我们的呼叫中心在 1,000 次模拟中的利润,看看每天的利润是如何变化的:

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

Profit Distribution of Our Call Center

哇,鉴于我们呼叫中心目前的运营指标,亏损的可能性非常高(近一半的模拟利润为负)。那我们该怎么办?

回想每位员工的业绩都遵循二项分布,我们意识到我们可以采取以下一项或多项措施来改善情况:

  • 多打冷门电话(增加 n )。
  • 以更高的百分比转换(增加 p )。
  • 少给员工发工资(我们不会这么做,因为我们人好)。

最终,我们开发了一个潜在客户生成工具,使我们的呼叫中心员工能够识别更有可能购买我们产品的人。这导致电话通话时间更短(需要的甜言蜜语更少),我们的转换概率上升,最终对我们的参数进行了如下修改:

  • n = 55
  • p = 5%

让我们运行下面几行代码,模拟我们新改进的呼叫中心的 1000 个潜在工作日,看看这些变化如何影响我们未来每日利润的统计分布。

# Call Center Simulation (Higher Conversion Rate)# Number of employees to simulate
employees = 100# Cost per employee
wage = 200# Number of independent calls per employee
n = 55# Probability of success for each call
p = 0.05# Revenue per call
revenue = 100# Binomial random variables of call center employees
conversions_up = np.random.binomial(n, p, size=employees)# Simulate 1,000 days for our call center# Number of days to simulate
sims = 1000sim_conversions_up = [np.sum(np.random.binomial(n, p, size=employees)) for i in range(sims)]
sim_profits_up = np.array(sim_conversions_up)*revenue - employees*wage# Plot and save the results as a histogram
fig, ax = plt.subplots(figsize=(14,7))
ax = sns.distplot(sim_profits, bins=20, label='original call center simulation results')
ax = sns.distplot(sim_profits_up, bins=20, label='improved call center simulation results', color='red')ax.set_xlabel("Profits",fontsize=16)
ax.set_ylabel("Frequency",fontsize=16)
plt.legend()

上面的代码还绘制了我们的改进结果(红色)相对于旧结果(蓝色)的分布。我们不需要进行 A/B 测试(尽管我们真的应该)就能看到我们的销售线索挖掘工具已经显著改善了我们呼叫中心的运营和盈利能力。

我们成功认识到每个员工产生的利润遵循二项式分布,因此如果我们能够增加 n (每天拨打的陌生电话数量)和 p (每次电话的转换概率)参数,我们可以产生更高的利润。

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

Improved Call Center Simulation

结论

最后,我想提出以下几点:

  • **概率分布是接近现实的模型。**如果我们能够找到一个与我们所关心的结果非常接近的分布,那么这个分布就非常强大——就像我们上面看到的那样,只需要几个参数(n 和 p ),我们就可以对数百人产生的利润进行建模。
  • 然而,同样重要的是认识到模型在哪里以及如何与我们的现实情况不匹配 —这样我们就知道在什么情况下我们的模型可能表现不佳。

干杯!

更多数据科学与分析相关帖子由我:

了解 PCA

了解随机森林

理解神经网络

了解 A/B 测试

用 Tableau 可视化股票

维度的诅咒

强化学习中的函数逼近

原文:https://towardsdatascience.com/function-approximation-in-reinforcement-learning-85a4864d566?source=collection_archive---------0-----------------------

当状态和动作空间爆炸时该怎么办…字面意思?

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

Photo by Maksym Kaharlytskyi on Unsplash

更新:学习和练习强化学习的最好方式是去 http://rl-lab.com

概观

在像 DP 和蒙特卡罗这样的表格方法中,我们已经看到状态的表示实际上是每个状态的记忆。
现在让我们回忆一下到底什么是状态。
状态是可观察特征或变量的组合。这意味着每当一个特性或变量有了一个新值,就会产生一个新的状态。

我们来举一个具体的例子。假设一个代理在 4x4 网格中,那么代理在砂砾上的位置就是一个特征。这给出了 16 个不同的位置,意味着 16 个不同的州。

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

Grid World

但这还不是全部,假设方位(北、南、东、西)也是一个特征。这为每个位置提供了 4 种可能性,这使得状态的数量为 16*4 = 64。此外,如果代理有可能使用 5 种不同的工具(包括“无工具”的情况),这将使状态数增加到 64 * 5 = 320。
你明白了……

表示这些状态的一种方法是创建一个多维数组,比如 V[row,column,direction,tool]。然后我们或者查询或者计算一个状态。
例如 V[1,2,north,torch]表示代理在第 1 行第 2 列,面向北方,手持火炬的状态。这个数组单元格中的值表明了这个状态的价值。
另一个例子是 V[4,1,west,nothing],它是第 4 行第 1 列的一个代理,向西行驶,但什么也没有。

让我们也考虑一下国际象棋。
每一步棋后棋盘的情况是一种状态。估计大约有 10^120 个州!
国际象棋中一种状态的一种表示可以是:

黑卒,黑车,…,无,无,…。,白皇后,白主教]

其中每个维度代表棋盘上的一个正方形,其值是黑色或白色棋子之一,也可以是零。

所以一旦我们有了状态集,我们就可以为每个状态分配一个值状态函数。

不用说,容纳多个状态所需的存储量是巨大的,并且计算每个状态的值所需的时间也是令人望而却步的。

从逻辑上讲,这促使我们寻找更好、更合适的解决方案。

解决方法

记住我们正在努力做的事情总是有用的,因为我们可能会忽略所有的细节。
我们的想法是,我们希望找到环境中每个状态/动作的值,以便代理遵循收集最大回报的最佳路径。

在上一节中,我们已经表明,当状态空间变得太大时,表格方法就变得不充分和不合适了。为了解决这个缺点,我们可以根据每个状态的特征采用新的方法。目的是使用这些特征集合来概括具有相似特征的状态的值的估计

我们使用单词估计来表示这种方法永远不会找到一个状态的真实值,而是它的近似值。尽管这个结果看起来很不方便,但是这将实现更快的计算和更多的推广。

计算这些近似值的方法被称为函数近似值

有许多函数逼近器:

  • 特征的线性组合
  • 神经网络
  • 决策图表
  • 最近邻

由于我们将使用梯度下降以找到最佳结果,函数逼近器必须是可微分的,这导致我们对特征和神经网络的线性组合。

特征的线性组合

现在让我们深入研究这种线性函数逼近方法的细节。
但首先让我们记住这个公式,它来自数学背后的强化学习文章:

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

这是一个递归公式,它根据下一个状态***【s’***等等的值来计算一个状态 s 的值…
这个公式的问题是,每次我们需要计算 V(s)时,我们都需要计算所有未来的状态。更糟糕的是,如果我们在某个时候遇到一个与我们过去已经见过的状态相似的状态,我们没有办法识别它。

还记得在时间差异文章中提到的,利用 Q 学习公式,我们有一种估计未来状态的方法:

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

我们将利用这些公式推导出一些有趣的解。
让我们重新定义 V(s ),比如它反映了它所包含的特征。状态值函数可以用其特征的加权和来表示:
V(s) = W1。F1(s) + W2。F2(s) + … +Wn。Fn(s)或简称:

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

𝝫(s)是在状态 s 下的特征向量,而𝜽ᵀ是应用于特征的权重的转置矩阵,以这种方式,一些特征在任何状态 s 下比其他特征更受重视。

现在让我们定义𝜹,也称为 TD 误差,如下所示:

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

通过用新的定义代替𝜹中的 V(s ),我们得到:

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

请记住,根据定义 V(St) = R(t₊₁) + 𝛾V(St₊₁),这意味着𝜹应该是零。然而,我们不是在一个确定的环境中,这意味着我们不能确定相同的行为总是导致相同的结果,并且学习过程不是 100%准确的,这意味着我们对 V(s)的学习不一定等同于 R(t₊₁) + 𝛾V(St₊₁).所有这些导致𝜹具有非零值。所以现在我们的目标是计算𝜽,以便最小化𝜹.通过最小化 J(𝜽).来实现这一点的便利方法

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

J(𝜽)与任何回归一样,计算实际结果与真实结果之间的差异。然而我们的问题是我们不知道什么是真正的结果!相反,我们将计算实际结果和估计结果之间的差异。每次迭代后,我们都有一个新的真实结果估计,这使得我们看起来好像是在瞄准一个移动的目标。
想法仍然是不断改进估计,直到两者之间的差异变得足够小。
已经证明这种方法有足够的收敛性保证。

成本函数的最小化可以使用梯度下降法来完成,该方法在梯度下降文章中有详细描述。
梯度下降将逐步向最小化 J(𝜽的值更新𝜽,更新公式为:

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

其中𝜹 𝝫(s)大致是 J(𝜽)相对于𝜽的导数,⍺是学习率]0,1]。

重要提示:实际上 J(𝜽相对于𝜽的推导是𝜹【𝝫(st)-𝝫(st+1】,但实际上这个算法的结果更差。

我们已经建立了状态值函数的函数近似,现在让我们把这个概念扩展到动作值函数。

设𝝫(s,a)为状态 s 的特征,动作 a ,我们要做的是根据一个策略 π来估计 Q(s,a)的值。

与状态值类似,动作值函数为:

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

𝜽权重的更新是:

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

神经网络

与非线性函数近似(如神经网络)相比,线性函数近似的主要缺点是需要良好的精选特征,这可能需要领域知识。

在非线性函数逼近器中,我们将再次重新定义状态和动作值函数 V 和 Q,例如:

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

所以现在 v 和 q 是非线性函数即以𝝫(s) / 𝝫(s,a)和𝜽为参数。
在这个公式中我们要注意的是,特征的数量和𝜽向量的大小不一定相同(就想象一个神经网络)。

从上面我们现在可以计算 TD 误差𝜹和权重𝜽的更新

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

请注意,𝜹是使用先前已知的𝜽值(此处称为𝜽⁻)和新计算的值来计算的。
神经网络计算的细节可以在反向传播文章第一部分第二部分第三部分中找到。

结论

总之,当类似情况发生时,函数近似有助于找到状态或动作的值,而在计算 V 和 Q 的真实值时,需要完整的计算,并且不从过去的经验中学习。此外,函数逼近节省了计算时间和存储空间。

相关文章

Scala 中的函数式编程特性

原文:https://towardsdatascience.com/functional-programming-features-in-scala-e770c7f31a6c?source=collection_archive---------21-----------------------

数据工程

Scala 语言的特性和模式

在过去的几个月里,我一直在探索使用 Scala 及其 eco 系统进行函数式编程。

在这篇文章中,我将重点介绍该语言的一些特性,这些特性支持为分布式系统和数据操作直观地创建功能代码。

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

Photo by apoorv mittal on Unsplash

高阶函数

根据官方文档,函数是 Scala 中的第一类对象,这意味着它们可以-

  • 把另一个函数作为参数,或者…
  • 返回一个函数

一个函数将另一个函数作为参数的例子是 Scala 的标准集合库中的map()函数。

val examplelist: List[Int] = List(2,9,8,14)examplelist.map(x=> x * 2) // anonymous function as argument

当使用标准的 Scala 集合时,链操作符也非常直观,尤其是使用中缀符号时。在下面的小代码示例中,我定义了一个从 1 到 20 的数字列表,过滤偶数,然后对它们求和。

(1 to 20).toList filter (_%2 == 0) reduce (_ + _)

_是通配符——在地图和过滤器的情况下,它指的是集合中的值。

递归

对集合中的所有项目进行操作的推荐方式是使用操作符mapflatMapreduce

如果这些操作符不能满足用例的需求,那么写一个尾递归函数来操作集合中的所有条目是非常有用的。

下面的代码示例显示了计算一个数的阶乘的尾递归函数定义。

import scala.annotation.tailrec@tailrec
// Factorial Function Implementation that uses Tail Recursion
def factorial(in_x: Double, prodsofar: Double = 1.0): Double = {
    if (in_x==0) prodsofar
    else factorial(in_x-1, prodsofar*in_x)
}factorial(5)

在 Scala 中,一个尾部递归函数,如上所述,可以被编译器优化(使用上面的@tailrec注释),只占用一个堆栈帧——所以即使是多层递归也不会出现 stackoverflow 错误。这是可能的开箱即用,不需要任何框架或插件。

如上所述,推荐的方式是使用集合运算符(如reduce等)。).作为集合 API 简单性的演示,上面的阶乘函数也可以由下面的 1-liner 实现

(1 to 5).toList reduce (_*_)

为了从概念上理解reduce,看看这个伟大的链接

(还要做查看foldLeftfoldRightmapflatMap的解释,了解一些常用的数据操作!)

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

Photo by Patrick Baum on Unsplash

案例类别

Case 类可以非常容易地实例化,不需要 boiler plate 代码,如下例所示。

case class BusinessTransaction(sourceaccountid: Long, targetaccountid: Long, amount: Long)// create some transactions now to demo case classes// I lend my friend
val 1_xaction = BusinessTransaction(112333L, 998882L, 20L)
// My friend pays me back 
val 2_xaction = BusinessTransaction(998882L, 112333L, 20L)

仅仅上面的 1 case class ..行做了以下有用的事情——

  • 定义了 3 个不可变的值sourceaccountidtargetaccountidamount
  • 定义 get 方法来访问构造函数参数(例如:1_xaction.amount)

虽然易用性很好,但 case 类是在 Scala 中存储不可变数据实例的推荐方式。例如,在大数据应用程序中,大数据文件的每一行都可以通过 case 类建模并存储。

使用 case 类存储数据的一个例子是这里的。

在链接的示例中,函数rawPostings将数据文件的每一行建模为 case 类Posting的一个实例。最终,它返回一个类型为RDD[Posting]的数据集。

模式匹配

在 Scala 中,case 类、常规类和集合等对象可以通过模式匹配进行分解。

您可以使用模式匹配来-

  • 分解对象的类型(如下例)
  • 获取集合的头(例如一个List或一个Seq)

下面的代码示例展示了如何使用模式匹配来分解一个Seq

val seq1: Seq[Int] = Seq(1,3,4,5,5)seq1 match {
    case x::y => println(s"The first element in the sequence is ${x}")
    case Nil => println("The sequence is empty")
}

cons 操作符(::)创建一个由头部(x)和列表其余部分(称为尾部,y)组成的列表。

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

伴随物体

在 OOP 中,一个静态变量有时被用在一个类中来存储多个实例化对象的状态或属性。

但是 Scala 中没有static关键字。相反,我们使用的是伴随对象,也称为单例对象。伴随对象是使用object关键字定义的,并且与伴随对象的类具有完全相同的名称。

伴随对象可以定义不可变的值,这些值可以被类中的方法引用。

在 Scala 中有两种使用伴随对象的常见模式

  • 作为工厂方法
  • 提供类共有的功能(即 Java 中的静态函数)
// The 'val specie' straightaway defines an immmutable parameter abstract class Animal(val specie: String) {
    import Animal._ // Common Behaviour to be mixed-in to Canine/Feline classes
    def getConnectionParameters: String = Animal.connectionParameter }object Animal { // .apply() is the factory method
    def apply(specie: String): Animal = specie match {
        case "dog" => new Canine(specie)
        case "cat" => new Feline(specie)
    } val connectionParameter:String = System.getProperty("user.dir") }class Canine(override val specie: String) extends Animal(specie) {   
    override def toString: String = s"Canine of specie ${specie}"
}class Feline(override val specie: String) extends Animal(specie) {
    override def toString: String = s"Feline of specie ${specie}"
} // syntactic sugar, where we don't have to say new Animal
val doggy = Animal("dog")
val kitty = Animal("cat")doggy.getConnectionParameters

选择

大多数应用程序代码检查 Null/None 类型。Scala 对空类型的处理略有不同——使用的构造称为Option。最好用一个例子来说明这一点。

val customermap: Map[Int, String] = Map(
    11-> "CustomerA", 22->"CustomerB", 33->"CustomerC"
)customermap.get(11)         // Map's get() returns an Option[String]
customermap.get(11).get     // Option's get returns the String
customermap.get(999).get    // Will throw a NoSuchElementException
customermap.get(999).getOrElse(0) // Will return a 0 instead of throwing an exception

在 Python 这样的语言中,if None:检查在整个代码库中很常见。在 Java 中,会有 try-catch 块来处理抛出的异常。Option s 允许关注逻辑流程,对类型或异常检查进行最小的转移。

在 Scala 中使用Option s 的一个标准方式是让你的自定义函数返回Option[String](或者IntLong等等。).让我们看看Map结构的get()函数签名-

def get(key: A): Option[B]

使用它的一个(直观的)方法是用如下所示的getOrElse()函数链接它

// Map of IDs vs Namesval customermap: Map[Int, String] = Map(
    11-> "CustomerA", 22->"CustomerB", 33->"CustomerC"
)customermap.get(11).getOrElse("No customer found for the provided ID")

使用Option s 的一个非常有用的方法是使用像flatMap这样的集合操作符,它直接透明地为你处理类型。

// Map of IDs vs Namesval customermap: Map[Int, String] = Map(
    11-> "CustomerA", 22->"CustomerB", 33->"CustomerC"
)val listofids: List[Int] = List(11,22,33,99)listofids flatMap (id=> customermap.get(id)) //flatMap magic

这就是我这篇文章的全部内容!我正在研究 Akka 和 Actor 模型的并发系统。在以后的文章中,我将分享我在这个主题上的心得(以及它与 Scala 函数式编程方法的关系)。

原载于http://github.com

函数式编程太棒了

原文:https://towardsdatascience.com/functional-programming-is-awesome-c94bcd150ae6?source=collection_archive---------5-----------------------

为什么函数式编程正在兴起

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

编程范例是一个用来描述编写命令的方法的术语。一门语言的理念是建立在它的编程范式之上的。最著名的三个范例是面向对象编程、命令式编程和函数式编程。没有一种意识形态比另一种更好,因为通常它更多的是关于使用正确的工具来完成工作。

函数式编程是一个大多数软件工程师至少模糊地熟悉的概念。有史以来第二种为计算机编程而编写的编程语言 Lisp 实际上很好地包含在函数范式中。根据简化的定义,函数式编程优先考虑数据的不变性和数学计算,而不是传统地修改存储在类构造函数中的部分对象。对于现代函数式编程,这种想法有点牵强,但这不一定是件坏事。函数式语言的可变性带来了更多的用途,我敢说:

功能

到函数式编程。记住这一点,函数式编程并不局限于函数式语言。例如,Python 具有功能特性。尽管传统上,函数式编程有着难以置信的不同,但函数式和面向对象的范式似乎与用于数据科学的大多数语言有点接近。

对我们的工作很有帮助

大多数函数式语言的标题中都有“统计”。这很方便,因为数据科学家很像统计学家,只是添加了编程和机器学习技能。对于数据科学家来说,函数式语言通常更快,最重要的是更容易。

相信我,你不会想浏览 C 代码,因为你的准确性有点低。大多数函数式语言都是完全可读的,并且很容易输入和掌握。一些人可能会惊讶地发现,函数式编程是许多互联网最古老的大数据管道的基础。随着机器学习和统计计算的兴起,函数式编程最近变得越来越流行。

酷语言

有很多很酷的统计语言,其中很多都有自己的特色和其他更传统的特色交织在一起。在函数式编程语言下,有很多我非常喜欢的很酷的语言。

朱莉娅

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

朱莉娅是我一直以来最喜欢的语言。尽管 Julia 确实是函数式的,但它确实包含了一些可变的、类似面向对象的属性,这使得它在编程上更加方便。作为一门高级语言,Julia 的速度快得令人难以置信。它很容易键入,如果键入得好,它可以和 C 一样快,同时比 R 和 Scala 更容易阅读。通常,在 Julia 中建立和训练一个模型并不需要很长时间,这增加了使用 Julia 的好处,因为语言很容易,ML 很快。在某些情况下,比如在我的 ML 包中,车床,机器学习可以用更少的行来完成,构造函数的属性可以用参数多态性更容易地变异。

咬舌

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

尽管 Lisp 不一定因其数据科学领域而出名,但它仍然是一种非常酷的语言。Lisp 和 Julia 一样,让编程变得非常简单和方便。一个真正值得注意的特性是 Lisp 的宏和 Julia 的宏。宏本身是一个主要的功能特性,如果使用正确,可以使笔记本编码变得非常容易。应该注意的是,Lisp 已经将自己分割成不同的语言集,包括 Scheme、Clojure 和(Common) Lisp。很难理解或估计第二高级语言及其功能的影响,但范围肯定是相当大的。

稀有

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

现在我们问候我们的老朋友,R. R 传统上是一种函数式语言,但像大多数其他语言一样是(或已经成为)多范式的,这意味着它从每个特定的编程范式中挑选自己喜欢的。这很好,因为它给了 R 可变性的优势。r 起源于 S 语言,并且一直专注于统计计算。

哈斯克尔

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

Haskell 与我上面谈到的多范式语言完全不同,Haskell 以其纯粹的函数性而自豪。我不能代表 Haskell,因为与列表中的其他语言不同,我从未使用过它。但是据我所知,Haskell 确实是一种很酷的语言。我对学习 Haskell 的担心纯粹是局限。正如我所讨论的,大多数现代语言都是多范式的,这使得它们可以有效地消除任何需要消除的错误,而无需创建新的代码库。

最后的想法

我大部分时间都在函数式语言中度过,主要是 Julia,如 Github 上的笔记本库所示,其中主要包含 Julia 笔记本。我喜欢函数式编程,因为它非常适合我所做的事情。当然,有些时候 Python 构造函数(类)可能更适合某项特定的工作,但总的来说,Julia 完成了这项工作,它的输入方式对我来说非常流畅和高效。对于其他人来说,函数式语言可能无法实现他们的目标,对于一生都在使用面向对象语言的人来说,学习函数式语言可能很困难。归根结底,语言是一种选择,大多数语言都有开发者,大多数语言都有优缺点。

什么是增强现实

原文:https://towardsdatascience.com/fundamentals-of-augmented-reality-ar-89ce986f70f8?source=collection_archive---------8-----------------------

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

Image by Dina Dee fromPixabay

什么是增强现实,它是如何工作的

增强现实(AR)是我们用数字数据增强的现实。

数字数据可以是文本、图片、视频、3d 资产或以上所有形式的组合。

AR 系统将需要理解现实并重建它,以创建它的数字双胞胎。

它还需要使用户能够与数字孪生和数字数据进行交互。

照相机没有固有的方向感。你不能命令它给你前面或后面的东西拍照。你只能把它指向你想要的方向。

这是一个传感器阵列,当光线穿过一个洞射向它时,它就会燃烧起来。

赋予相机方向感和对世界的理解是一个复杂的过程。处理分析和理解像素的研究领域被称为计算机视觉(CV)。

像我们一样看世界,像我们一样分类物体,像我们一样学习适应世界的变化,这就是机器学习(ML)。

我们不断学习,变得更加聪明。当机器学习并产生比以前好一点的结果时,它的人工智能(AI)。

ML、AI 和 CV,以及组成 IMU(惯性测量单元)的一些硬件传感器,使 AR 成为可能。

AR 的层次

增强现实应用的核心是摄像头。一切都来源于摄像机带入系统的数据。

通过检测形状和运动来形成增强世界。这构成了物理层。

除了有形的层面,还有赋予我们所见事物意义的语义层。

语义层包括训练神经网络,以便机器能够像我们一样理解世界。

自动驾驶汽车在很大程度上解决了这个问题。这些汽车上的硬件比手机上的更先进。在移动领域,我们仍在努力构建理解物理世界的基础层。

这篇文章是关于物理层的。

物理层

物理层的主要组件是-

  1. 惯性测量组合
  2. 用户交互
  3. 点云和空间制图/ SLAM
  4. 模式匹配
  5. 锚,跟踪和坚持。

1.惯性测量组合

IMU(惯性测量单元)是测量线性加速度的加速度计、测量旋转的陀螺仪和测量航向的磁力计。

汽车上的传统里程表通过检测车轮的转动来测量行驶的距离。

手机上的视觉里程表从一系列图像中测量行驶的距离和方向。

视觉里程计与 IMU 的组件相结合,形成视觉惯性里程计(VIO)系统。

我们不能使用传统的里程表传感器,因为它们是为车轮设计的,而我们的是腿部运动。

2.用户

我们不需要智能眼镜来看世界,就像我们不戴眼镜时一样。

AR 的用处来自于使用关于世界的信息,然后给它增加价值。

博物馆里的灭绝动物可以活过来,或者将我们的相机对准建筑物可以向我们展示更多信息。如果你去伦敦,你现在看不到大本钟,因为伊丽莎白塔还需要几年才能完工。但是,如果你下载大本钟 AR 应用程序,并将其指向脚手架负载的结构,你就可以再次看到和听到大本钟。

3.点云和空间制图/ SLAM

来自相机的数据由计算机视觉算法进行分类,这些算法试图识别数据流中的现实世界物体,并返回一堆点,也就是点云。

拿一个放大镜仔细看一张图片,你会开始看到像素。或者,如果你在 Photoshop 中不断放大图片,你会开始看到像素。这张照片只不过是一个点云。在图片的情况下,这些点被称为像素。

图片中的像素以某种方式组织和排列在一起,使得从远处看,图片是完整的。手机上的点云数据还没有整合起来,因为它仍在试图理解现实世界中的图片。

连接云中的点会产生一个网格。看起来像蜘蛛侠用蜘蛛网覆盖你的家具。随着新的信息通过摄像机进入,网格不断变化并一直扩展,直到系统可以开始锁定对象。这也称为空间映射。

在绘制环境地图的同时,该程序还试图定位自己,以找出设备相对于周围环境的位置以及它面对的方向。这被称为 SLAM(同步定位和绘图)

然后程序试图在这个网格中找到熟悉的模式。这就把我们带到了下一个话题——模式匹配。

在下面的参考部分有一个来自微软的视频,展示了空间映射如何在 Hololens 上工作。

4.模式匹配

苹果的 ARKit 和谷歌的 ARCore 的第一次发布,移动上的两个原生 AR 框架只识别水平面。这两个框架花了几个月才识别出垂直面。

水平面为我们提供了放置数字对象的地面和桌面。

很多 AR 游戏非常有创意的使用了水平面。现在很有趣,因为它是新的。除了游戏,要让 AR 在现实生活中有用,我们需要的不仅仅是飞机。

除了程序检测形状,我们可以告诉程序寻找一个复杂的模式。该图案以图像的形式提供给系统。

图像识别

我们可以用图像识别做很多事情——比如当你把手机对准广告牌上的数字动画人物时,你就可以把这个人物放在手机屏幕上。让博物馆里的图片活起来,或者提供关于艺术家的信息,等等。

物体、房间和场景识别

我们还能够扫描 3D 对象,并为程序生成一个模式,然后识别现实世界中的 3D 对象。这叫做物体识别。

如果您将对象识别扩展到一个房间中的多个对象,它将成为房间识别。

在户外更大范围内进一步扩展,就成了场景识别。

Wikitude 支持物体、房间和场景识别。ARKit 在其最新版本中支持对象识别 it,并且用例有些受限。扫描 3D 对象以将其提供给系统的必要步骤需要时间来在消费硬件上正确完成。

虽然能够检测比平面更复杂的图案是件好事,但是这种方法有一个严重的缺点。

喂食模式限制了程序真正了解世界。如果我们要从每一个可能的角度给我们当今世界的每一张照片提供信息,这将需要很多年和数十亿美元,而且一旦我们告诉程序一张照片,这张照片可能已经过时了。

我们这个世界的事情一直在变。

一天中的时间变化,季节变化,灯光变化,使事物看起来不同。山在冬天会下雪,看起来和夏天不一样。同一棵树也会落叶。事情会随着时间而褪色。新的拆迁。新的结构,我们可以用几年的时间来写这段文字,写下所有发生变化的东西。

5.锚定、跟踪和持久性

很多人用持续这个词,其实是指跟踪一个主播。然后他们问了一个关于堆栈溢出的问题,并以错误的理由否决了一个响应。

让我们先弄清楚一些定义。

锚定是将数字对象放置在增强世界中。

跟踪是在相机四处移动时保持增强世界的一致定义,以便增强世界中的数字对象停留在它们锚定的位置。

但是它在追踪什么呢?

它从图像序列中跟踪特征点,通常是高对比度的点,以形成光流。ARKit 结合使用这种基于特征的方法和另一种称为直接方法的方法来完成工作。

关于特征点和从图像中提取特征的更多信息可以在参考文献部分找到。

持久性是指当应用程序关闭并再次打开时,能够记住数字对象在现实世界中的位置。

持久性的工作原理是保存(序列化)我们从世界点云中获得的地图。当您打开应用程序时,您可以选择重新加载保存的地图。这样,您就不需要在该空间中再次执行空间映射。

人类视觉系统中的术语“持续性”与大脑在图像被取走后保持图像几分之一秒的能力有关。这就是电影的工作原理。对于我们来说,每秒 24 帧是一个很好的帧速率,可以保留上一帧并将其融合到下一帧中,以获得流畅的运动感。但是,那不是 AR 中的坚持。那是追踪。

模式匹配的局限性

当我们 2 岁的时候,我们看着一个图案,然后指着它。一个声音轻轻地告诉我们,这是一张桌子!表!然后我们看了其他 100 张桌子,被告知是桌子!

稍微不同的形式但共同的特征可以用共同的标签来标记。在某种程度上,我们开始对桌子充满信心。

手机上的 AR 很难整理出飞机在现实世界中可能出现的所有不同方式。

一个水平面可以处于许多不同的高度,在不同的光照条件下,它们看起来可能不同。

垂直面可以有多种不同的角度。

可以有任意数量的既不水平也不垂直的倾斜平面。

墙从地面升起。45 度的墙。如果我们沿着边走,现在我们是沿着一个多边形而不是一个矩形。并且墙壁彼此相交。他们总是这样。

随着摄像机花费更多时间收集数据,程序的置信度可能会上升或下降。

随着新数据的到来,也许那架飞机现在看起来更大或更小,或者位于大厅更远处。来自位于第一架飞机后面 50 英尺的另一架飞机的数据进入,现在从摄像机的角度看,这两架飞机看起来像一个巨大的垂直平面。

随着时间的推移,也许程序将能够解决飞机是分开的。但是我们没有更多的时间了。我们希望程序能非常快地完成所有这些计算。要让 AR 在手机上有用,它必须实时工作。

跟踪的局限性

随着对相机周围世界的理解发生变化,世界的定义也发生变化,程序越来越难准确地记住数字世界中特定对象的位置,尤其是当它不看着它时。

在现实世界中,这个问题可能是这样的

我看到一台自动点唱机,就走过去在酒吧找了个座位坐下。现在我知道点唱机在我身后。也许我会点一杯啤酒,放在我正前方几英寸的地方。如果这是菲利普·K·蒂克的小说,也许这就是当我眨眼的时候世界改变的时候。世界绕着它的轴旋转,也许我忘了和它一起旋转。我伸手去拿啤酒,它已经不在我刚才放的地方了。点唱机也不是真的在我身后。它现在在我的左边!

想象一下你周围的一切都有可能像这样每秒钟移动 30 次!(默认情况下,手机上的摄像头以每秒 30 帧的速度录制视频)。

我们需要在这些芯片上有大量的能量,以便能够实时处理这个世界并把它保持在一起。

结论

我们的大脑是我们的硬件,我们的智力是我们的软件,已经进化了一百万年。相机已经有 100 多年的历史了,机器学习比它还新。

要做好这件事有很多挑战。云计算是真实存在的,随着 5G 网络速度的到来,我们应该能够使用云的力量,并将结果实时传输回手机。

AR 在教育、旅游、时尚、零售、公用事业、娱乐、出版、MRO、远程指导、游戏等领域有着巨大的潜力……这个清单很大,而且还在不断增长。

数十亿美元正涌入这项技术。一些人认为这是继互联网之后的下一个最大的革命。

Magic Leap 是一家制造 AR 耳机的公司,它成为了科技史上获得资金最多的初创公司——据报道,风投资金达 23 亿美元,而且还是在没有产品的情况下。当它结束融资时,它所拥有的只是展示潜在产品的视频。现在他们有了一个产品。这是好的,但远远达不到他们在那些视频中显示的。后来他们声称这些视频是在视觉特效软件中被篡改的。不知道他们是怎么逃脱的。

但是,这是当今 AR 创业世界存在的狂热。没有投资者想错过。

无论财务预测是什么,最终,我相信它会改善我们联系的方式。

半个世纪以来开发的复杂计算机视觉算法可以实时检测面部特征。Snap 使用该算法覆盖数字图像,用户以有趣的方式使用它来建立关系和人际联系。

令人愉快的体验来自看似互不关联的技术层面。

在实验室深处智能开发的扫描像素技术(CSAIL)可以通过帮助人们以新的方式交流来获得其意义,并在将他们与世界联系起来的事物中找到新的意义。

对我来说,这是一个完整的循环,这也是我所期待的。

参考

从图像中提取特征;

[## 特征提取-维基百科

在机器学习、模式识别和图像处理中,特征提取是从一个初始集合开始的

en.wikipedia.org](https://en.wikipedia.org/wiki/Feature_extraction)

微软 Hololens 空间制图:

机器学习基础(三)

原文:https://towardsdatascience.com/fundamentals-of-machine-learning-part-3-b305933f00cd?source=collection_archive---------16-----------------------

信息论

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

介绍

这是我认为机器学习的基础主题系列的第 3 部分。这些主题是加深对该领域理解的基础。到目前为止,我们已经介绍了:

  1. 概率论
  2. 最大似然估计

这篇文章的目的是涵盖信息论中的重要概念,并描述它们如何用于机器学习。这里的许多主题将建立在我们在概率论的文章中讨论的概念上,比如独立性和期望。

我将利用机器学习和统计学的思想来激发和构建这些概念。如果你有物理学背景,你可能已经看到这些概念的不同动机(即通过热力学)。

信息

让我们假设我们的目标是通过使用的单词来检测给定文本的作者,以此来激发我们的讨论。哪些词对检测作者身份有用?直觉上,像“the”、“or”和“it”这样的词不会很有用,因为无论作者是谁,这些词都很有可能出现在任何文本中。看起来普通单词包含的信息比罕见单词少,而且信息内容在某种程度上与概率直接相关。

让我们通过让 x 是代表所有可能单词的随机变量, p ( x )是观察一个特定单词的概率 x 来使这个更正式。我们想要为通过观察一个特定的单词所获得的信息创造一些可量化的概念。

我们将使用 h ( x )来表示从观察单词 x 中获得的信息。同样,对于两个单词 xy ,我们可以将通过观察它们获得的信息写成 h ( x,y )。我们的目标是找到一个合适的 h。

首先,我们来考虑两个词 xy 的独立性。像“足球”和“比分”这样的词不是独立的,因为看到一个会使另一个更有可能。所以在“足球”之后看“比分”获得的信息应该不大。回想一下第一部,独立在形式上是指 p ( x,y)= p(x)p(y)。如果两个词 xy 不相关,我们要 h ( x,y)=h(x)+h(y)。换句话说,同时看到两个不相关的单词所获得的信息应该是单独看到它们所获得的信息的总和。

我们希望我们的 h 公式与 *p,*相关,但是我们有这样一个问题,即独立观察的联合概率是一个乘积,而联合信息是一个和。为了解决这个问题,我们使用对数的乘积规则将乘积转换为总和:

log[ p ( x,y)]= log[p(x)p(y)= logp(x)+logp(y)。

将信息与概率的对数联系起来似乎是我们需要的技巧。我们把与 x 相关的信息写成:

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

Information gained from observing x.

这个公式满足我们的要求,即更可能的观察产生更少的信息,对于两个独立的观察 xy ,我们有 h ( x,y ) = -log p ( x, y)=-log[p(x)p(y)]=[-logp(x)]+[-logp(y)]=h()

对数的底是一个任意的选择。信息理论家通常使用基数 2,并将信息单位称为“比特”,而在机器学习中,通常使用基数 e 并将信息单位称为“NAT”。这个信息量的另一种观点是,给定某种编码*,hh(x)给出了发送一条消息 x 所需的位数。*

我们刚刚把信息写成了一个随机变量的函数。在第 1 部分中,我们了解到随机变量 x 上的函数 g 的期望值,给出了 g 的期望值(或平均值):

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

Expectation of g(x) with respect to x.

我们的信息函数的期望 h 是信息论中最基本的概念之一。这种期望被称为随机变量 x 的熵,通常用大写字母 H 表示:

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

Entropy of random variable x.

熵是信息函数的期望值,或者换句话说,它是通过观察从 p ( x )随机抽取获得的平均信息量。这是一个需要知道的有用值!

信息论者可能会使用熵来计算传输给定长度的消息所需的预期比特数。更好的是,为了降低传输成本,找到使熵最小化的消息编码将是有益的。这将允许发射机压缩消息,并且每条消息发送更少的比特。

寻找有效的编码类似于为机器学习问题寻找特征的有效表示。我们希望用尽可能少的特征来表示我们的数据(以避免过度拟合和维数灾难),同时仍然保持足够的信息来做出准确的决策。

我们将会看到,熵的概念对于思考其他几个重要的概念是有用的。

交叉熵

让我们回到我们的作者分类问题。让我们通过只考虑两个类来简化问题:{文本来自作者 A,文本不来自作者 A}。我们将把我们试图分类的每个文档视为一个“单词包”,这意味着我们不关心单词的顺序,只关心哪些单词存在。我们将每个文档表示为向量 x ,其中如果单词 j 出现在文档中,则条目 j 为 1,否则为 0。

我们假设存在某种基本事实分布 p ( x ),这给出了文档 x 是由作者 a 编写的概率。如果我们可以了解这种分布,那么对于任何一个其中 p ( x ) >为 0.5 的文档,我们都可以猜测该文档是由 a 编写的

但是我们不知道 p ,所以我们写下一个模型 q 并尝试拟合 q 的参数,使其接近*p。*然后我们将使用 q 来决定 A 是否是任何给定文本的作者。

由于我们使用的是 q ,因此对一个文档 x 进行分类所需的信息量为h(x)=-logq(x)。但是如果 x 实际上是由 p 分配的,那么对于xt所需信息的期望值是:

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

Cross Entropy

这是 q 相对于真实分布 p 的交叉熵。如果我们以此为代价函数,找到使交叉熵最小化的 q 的参数,那么当 qp 相同时,将出现最佳解。

最小化交叉熵通常是逻辑回归和用于分类的神经网络的目标。可以看出,最小化交叉熵相当于最大化我们的模型 q. 的似然性。因此,这种解决分类的方法找到了最大似然估计量,如第 2 部分中所讨论的。

相对熵(KL-散度)

假设我们使用的是 q 而不是 p ,交叉熵给了我们分类文档所需的平均信息量。显然,如果我们使用 p,,那么平均起来,我们只需要 H (x)比特数(真实分布的熵)。但是由于我们使用的是 q ,平均需要 H ( p,q )位(交叉熵)。

使用 q 差多少?用 q 代替 p 需要 H ( p,q )- H (x)额外的位。我们称这个值为相对熵或 Kullback-Leibler (KL)散度。如果我们代入交叉熵和熵的方程,然后收集各项,我们得到 KL 散度为:

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

KL-Divergence

同样,这给了我们所需的额外位数(或 NAT ),因为我们使用了 q 来代替 p 。KL-散度总是大于 0,除非 q 等于 p 。因此,最小化也是一个常见的目标函数。最小化 KL-散度也最小化交叉熵,我们已经说过交叉熵是最大似然估计量。

交互信息

我们之前讨论过,有些单词并不是相互独立的,当我们同时观察它们时,这种依赖性会导致我们获得多余的信息(例如“足球”和“得分”)。我们想知道两个变量共享多少信息。

回想一下,如果 x 和y 为自变量,那么它们的联合分布为 p (x,y) = p (x) p (y)。如果它们不是独立的,那么我们就不能这样分解联合分布。当变量独立时,我们没有任何冗余,随着变量越来越依赖,冗余信息量增加。为了对此进行量化,我们使用 x 和 y 的互信息:

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

Mutual Information.

如果我们用独立因子分解来表示真实的联合分布,那么互信息就是所需的额外信息量。如果变量是独立的,那么 KL 散度将为 0,否则它将随着变量冗余度的增加而增加。

互信息经常被用于在机器学习中执行特征选择。对于给定的特征,我们可以用类别标签来度量特征的互信息。如果互信息高,则该特征是该类的强指示符。例如,如果作者 A 总是在他们的文档中包含他们的名字,那么他们的名字和类之间的互信息将会非常高。

类似地,如果我们有太多的特征需要考虑,我们可以使用特征之间的互信息来删除那些冗余的特征。如果作者 A 总是包括他们的名字和他们的家乡,那么我们可以安全地从我们的词汇中删除他们的家乡,并且仍然可以很好地完成任务。

结论

在这篇文章中,我们已经涵盖了信息论中直接适用于机器学习的主要概念。从任何意义上来说,这都不是一个详尽的处理方法,但是这些是我个人在实践中一次又一次看到的概念。

更多阅读,我建议看看克里斯托弗·毕晓普的书模式识别和机器学习。整本书是知识的金矿,但是我讨论的概念都可以在第一章的信息论部分找到。

大卫·麦凯的书信息论、推理和学习算法也很受欢迎。

下次见!

数据库:SQL 基础

原文:https://towardsdatascience.com/fundamentals-of-sql-d0182bc1346b?source=collection_archive---------25-----------------------

带有客户交易实例的简单易懂的解释。

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

这篇文章不可能完全涵盖 SQL 功能的每个方面,但是,我试图创建一个快速参考指南,提供 SQL 的实用方面,并以简洁的方式展示重要的功能。如果你发现任何错误,请毫不犹豫地评论出来。

第 1 级

选择、从、在、分组依据、拥有、排序依据

当我们编写 SQL 语句时,它们的顺序应该和上面写的一样。我们还可以看到下面的代码:

从< 中选择
条件>*
其中< 条件> 分组按 <条件><条件>
顺序按< 条件>

然而,在执行方面,流程是不同的。这意味着,当 SQL 语句在服务器端运行时,其读取顺序与下面写的顺序相同:

FROM(它选择所需的表)
WHERE(它根据条件过滤行)
GROUP BY(它聚合过滤的结果)
HAVING(聚合值的附加过滤级别)
SELECT(从结果中选择适当的列)
ORDER BY(按升序或降序排列值)

示例:

SELECT OrderQty, Count(*) AS countsFROM Sales.SalesOrderDetailWHERE UnitPrice > 1000GROUP BY OrderQtyHAVING count(*) < 500ORDER BY counts desc;

结果:

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

结果显示,对于单价大于 1000 的所有订单,我们有 481 个数量为 6 的订单实例(意味着 6 个项目)和 266 个数量为 7 的订单实例,依此类推。因此,数量越高,计数越低。

第二级

子查询,别名

我们从子查询中得到的结果被传递给主查询,以便生成复杂且更精确的结果。此外,别名用于为列或表提供临时名称。让我们看看使用这两种方法生成结果的例子。

SELECT OrderQty, LineTotalFROM Sales.SalesOrderDetail AS s1WHERE UnitPrice = (SELECT MIN(UnitPrice) FROM Sales.SalesOrderDetail AS s2WHERE s1.SalesOrderDetailID = s2.SalesOrderDetailID )

这里的子查询返回每个特定 ID 的最小单价。因此,如果 1 的 SalesOrderDetailID 有 5 件商品,它将返回所有这 5 件商品中的最低价格,ID 2 也是如此。

然后,主查询将返回单价与子查询输出的单价匹配的订单数量和行合计。s1 和 s2 称为别名,对于连接表非常有帮助。

结果

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

这意味着,我们有一个实例,其中数量为 1,行总计为 2024.99400,属于一个 ID 的最小单价。然后是另一个数量为 1 的实例,具有基于最低价格的特定行合计。

三级

在,存在,之间

IN 子句可用于子查询,如下所示:

SELECT FirstName, LastNameFROM Person.PersonWHERE BusinessEntityID IN (SELECT BusinessEntityID FROM Sales.SalesPerson WHERE SalesLastYear > 2000000)

在此语句中,我们仅从 person 表中获取那些 BusinessEntityID 也存在于子查询中的人员的姓名,并且子查询从 Sales 表中返回销售额高于 2000000 的 business 的 BusinessEntityID。

EXISTS 子句可用于子查询,如下所示:

 SELECT AccountNumber FROM Sales.Customer AS c1 WHERE EXISTS (SELECT * FROM Sales.SalesOrderHeader AS SOH WHERE SOH.CustomerID = c1.CustomerID AND OnlineOrderFlag=1)

子查询只返回布尔值,而不是可以与其他列匹配的某些值。因此,只要在线订单被标记为 1,它就返回 TRUE,否则返回 FALSE。换句话说,外部查询仅基于子查询提供的 TRUE 和 FALSE 条件工作。

介于两者之间的例子

SELECT SalesOrderDetailID, UnitPrice FROM Sales.SalesOrderDetail WHERE OrderQty BETWEEN 4 AND 6

它只是检查订单数量在 4 和 6 之间的行。

四级

窗口功能&案例报表

案例陈述真的很酷。

这仅仅是一个 if-then-else 语句。它检查一个条件,如果它是真的,那么它返回一个特定的值,如果不是,它移动到 else 部分。我们可以定义许多 if-then 语句和一个 else 语句。只要遵循 SQL 的规则,就可以在整个脚本的任何地方使用它

SELECT  SalesOrderID,CASE OrderQty WHEN 1 THEN 'Order quantity is 1' WHEN 2 THEN 'Order quantity is 2' WHEN 3 THEN 'Order quantity is 3' ELSE 'Greater Than 3'END AS Order_QuantityFROM Sales.SalesOrderDetail

上面的代码只是说,选择 OrderQty 列,如果它是 1,则打印“order quantity is 1 ”,并对其他列进行类似的操作,直到您点击 else,其余的值将被打印为“大于 3”

窗口函数对于销售、利润和其他指标的比较非常有用。

这是我们将对其应用窗口函数的真实表的快照。

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

 SELECT OrderQty,AVG(UnitPrice) OVER(PARTITION BY OrderQty) AS average,MAX(UnitPrice) OVER(PARTITION BY OrderQty) AS maximum,MIN(UnitPrice) OVER(PARTITION BY OrderQty) AS minimum FROMSales.SalesOrderDetail;

这是结果的快照。

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

第 5 级

存储过程和 IF-ELSE (T-SQL)

当我们不得不反复编写一段代码时,存储过程是很有用的。相反,我们可以创建一个存储过程,定义一个参数,然后使用 execute 命令执行该过程。我们定义的变量写在@之后,然后用在 WHERE 子句中。

CREATE PROCEDURE attempt @cost_limit int AS SELECT * FROM Production.ProductCostHistory WHERE StandardCost > @cost_limit and endDate is null GOEXECUTE attempt @COST_LIMIT = 10

结果给出了标准成本大于执行期间传递给 cost_limit 变量的值的所有值(在本例中为 10)。

下一个例子

首先,我们声明变量,然后,我们启动 IF 语句并检查事务类型的计数是否大于 1。如果条件为真,将执行 BEGIN 部分。SET 关键字用于将 select 语句的值存储到变量中。一旦设置了变量,我就使用 print 语句通过 SQL 的串联功能打印变量值。请注意,CAST 函数在转换变量的数据类型时非常有用。

DECLARE @TYPE1 intDECLARE @TYPE2 intDECLARE @TYPE3 intIF(SELECT COUNT(*) TransactionType FROM Production.TransactionHistory) > 1BEGINSET @TYPE1 =(SELECT COUNT(*) FROM Production.TransactionHistory WHERE TransactionType = 'W');SET @TYPE2 =(SELECT COUNT(*) FROM Production.TransactionHistory WHERE TransactionType = 'S');SET @TYPE3 =(SELECT COUNT(*) FROM Production.TransactionHistory WHERE TransactionType = 'P');PRINT 'Total count of W type transactions ' + CAST(@type1 as varchar(10)) + '.' ;PRINT 'Total count of s type transactions ' + CAST(@type2 as varchar(10)) + '.' ;PRINT 'Total count of p type transactions ' + CAST(@type3 as varchar(10)) + '.' ;END

使用存储过程和 IF-ELSE 函数输出结果的附加查询。

CREATE PROCEDURE Learning@quantity int,@cost INTASBEGINDECLARE @COUNT INTSET @COUNT =(SELECT count(*) from Production.TransactionHistory where Quantity > @quantity and actualcost > @cost)PRINT @COUNTIF @count > 1000BEGINPRINT 'There are more than 1000 instances with quantity =' + cast(@quantity as varchar)+ ' and cost =' + + cast(@cost as varchar)ENDELSEBEGINPRINT 'THERE ARE LESS THAN 1000 such INSTANCES'ENDENDGOEXECUTE Learning @COST=10 , @QUANTITY = 10

请注意一些要点

  • 取决于子查询输出的布尔值。
  • IN 取决于子查询输出的匹配值。
  • 在 SQL 中,子句的顺序很重要,它根据执行的顺序而不同。
  • 为存储过程定义的变量不同于声明的变量。
  • 任何内容都可以放在开始和结束之间,并作为单个批处理运行。

支持竞争的神经网络

原文:https://towardsdatascience.com/funderstanding-competitive-neural-networks-f855bd7882e1?source=collection_archive---------17-----------------------

几年前,我想出了一个小术语来表示理解复杂概念的有趣方式。典型的大学教学方式是打好理论基础(无聊的几个小时),重复你需要知道的基本科目(无聊的几个小时),然后最终对正在发生的事情做出解释(又无聊了几个小时),之后你离开时不会比开始时有更多的理解。与此相反,当你试图资助某事、 时,你先从一个有趣的激励性例子开始,然后再深入探究这一切的原因和方式。

这是三个系列中的第一个帖子,旨在为一个特别有趣的主题提供“有趣的介绍”:竞争神经网络及其在向量量化中的应用(请,停止运行,我知道这听起来像是沉重的数学负担,但我保证我会尽量减少!).在第 2 部分中,我们将讨论所谓的自组织特征映射(SOFMs ),之后,我们将在第 3 部分中研究不断增长的神经气体。

矢量量化:总体思路

想象你有一张黑白图像。实际上,您可以将这样的图像看作是一个点坐标列表 (x,y) ,其中包含了您想要涂成黑色的每个点。然后你会接近一个网格,就像在一个正方形的数学练习本上一样,然后给列表上的每一点涂上颜色。显而易见,你的点数越多,你的清单就越长。对于这个例子,我将我的工作牌头像数字化,并为它创建了这样一个列表,密度相对较低——它给了我大约 15,000(!)积分:

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

My work badge photo, digitized, converted to black and white and thresholded for optimum performance.

问题是,我们能减少点数,仍然得到可识别的、语义上忠实的图像吗?一种解决方案是找到能够很好地代表其附近一系列点的点,并使用它们作为这些点的“代理”。我们不使用 15,000 个点,而是指定几百个新点,并将它们放置在适当的位置,使它们各自相对代表周围的点。这将允许我们用最近的新点替换所有这些点,有效地将 15,000 个点减少到几百个点。此外,我们可以创建一个新点列表,这通常被称为“代码簿”,而不是每次都必须记下坐标,我们只需用最近的代码簿条目的索引替换每个点的 (x,y) 坐标对。如果我们已经很好地设置了点数,那么我们将得到一个相当不错的近似值。

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

The underlying idea of vector quantization: represent a large number of vectors by a smaller number of entries in a codebook, each of which is a relatively good proxy for the points it represents.

只剩下一个问题:我们到底该如何做?我的意思是,好主意,但我们需要找到这些点的方法,对吗?当然,最简单的方法是定义一个误差函数,并不断优化,直到我们达到目标。

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

The Growing Neural Gas algorithm converges to the point set relatively quickly: in 20 iterations, it created a semantically comprehensible representation, still with only 2,500 points — 16% of the original 15,564 points.

一种方法是简单地丢弃随机点,计算它们代表多少新点,以及它们的邻域中有多少点已经被代表,而它们的邻域中有多少点根本不应该被代表。然后,我们不断增加新的分数,剔除表现不佳的分数。这通常类似于两种较老的机器学习方法,称为简单竞争赫比学习。问题是这些需要很长时间才能融合。我指的是年龄——大多数时候,结果并不令人印象深刻。

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

The idea of Delaunay triangulation: to divided the set of points into triangles so that no point is within the circumcircle of another triangle (gray). By definition, this also means a Delaunay triangulation does not have intersections between the triangulating lines. Illustration courtesy of Wikipedia/Gjacquenot.

相反,我们可以做得更好。我们有一些数学技巧可以帮助我们做到这一点,叫做三角测量。三角剖分基本上是以一种特殊的方式将一个空间分成三角形。对于点,最简单的三角剖分当然是开始连接点,直到你得到很多三角形。事实证明,有更聪明的方法可以做到这一点。 Delaunay 三角测量在多个点之间创建三角形,这样没有其他点在任何三角形的外接圆(包含三角形所有三个点的圆)内。这给了我们一个不相交的三角形网格。一个奇妙的小副作用是,如果我们连接外接圆的中心,我们会得到由这些点围成的空间的所谓的 Voronoi 分割。将画出每个 Voronoi 分割的边界,使得点 P 周围的 Voronoi 分割将包括比初始点集内任何其他点更靠近 P 的每个点。这有助于我们在点之间很好地划分空间:我们可以通过简单地测量代码簿点网格内 Voronoi 分区内的点的百分比以及空的百分比来测量我们模型的有效性。这使得误差函数相对容易优化。一件好事是,Delaunay 三角剖分和 Voronoi 分割都可以很好地推广到高维空间,所以在二维空间中工作的东西也可以在高维空间中使用。

变得有竞争力

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

A Growing Neural Gas model learning the topology of the Starschema logo, 100 iterations with a high drop-out rate.

好吧,那么“竞争性”神经网络呢?你可能遇到的大多数神经网络都遵循一定的模式:具有激活功能的神经元获得输入并产生输出。从这个意义上说,“竞争性”神经网络非常不同。相反,在竞争性神经网络中,神经元“竞争”被激活,其中激活通常是离所选数据点的距离的函数。最靠近数据点的神经元——也就是说,具有最高激活度的神经元——“获胜”,并向数据点移动,吸引其一些邻居。从这个意义上来说,竞争力允许学习拓扑,这是一个有用的副作用,当用于从低维表示重建高维形状时。事实上,Martinetz 和 Schulten 创建了第一个简单的神经气体模型,称之为拓扑表示网络 (TRN)。这篇由 Marco Piastra 撰写的论文展示了竞争性神经网络在重建甚至非常复杂的形状方面令人印象深刻的潜力,例如 22 类七面体或斯坦福兔子

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

Neural gas models can learn fairly complex topologies, such as the human face, as seen before. In this example, a high-dropout slow-converging Growing Neural Gas starting with two vertices and adding a new one every ten iterations creates an approximation of my photo. This graph contains only 752 nodes, a compression of 95.17% of the original 15,564 points.

竞争学习有一个相当接近的神经生理类比。它是 Hebbian 学习的一种形式。这种机器学习的方法源于对“一起放电的神经元连接在一起”的观察:也就是说,当独立的神经元同时对刺激做出反应时,突触强度(神经元“接近度”的一种衡量标准)就会增加。正是神经元的这一特性被不断增长的神经气体用来连接各个顶点,使我们不必像自组织特征图那样指定图的大小和形状:最佳匹配和第二最佳匹配被连接为“共同响应者”,连接的强度取决于两点之间的相对响应强度。如果两个点都强烈响应,则表明它们与数据点的距离几乎相等,因此可以被视为紧密相连——“一起触发”会导致算法将相关点“连接在一起”。

虽然听起来可能很抽象,这些算法可以很容易地适应我们日常机器学习工作量的一部分。在无监督学习的许多应用中,增长的神经气体和自组织特征图是有用的,其中第一个也是最重要的当然是聚类。但与许多聚类算法不同,例如,生长神经气体不需要预先提供聚类数。这在不相交的簇的数量是问题的开始时是有用的。例如,考虑计算页面字数的问题:增长的神经气体,如果配置良好,可以将单词加入到单独的子图中,不相连的子图的数量给出了字数。类似地,难以可视化和解释的多个高维集群可以使用基于增长神经气体的算法轻松计数。

我们将在本系列的后续部分中讨论的两个实现——生长神经气体自组织特征映射 (Kohonen 映射)——具有广泛的用途。正如我们将看到的,它们不仅可以用于复制形状,还可以用于聚类和嵌入高维数据集。因为这些技术属于拓扑数据分析的更广泛的领域,这可能变得非常数学密集,并且因为它们不同于我们通常理解的神经网络的方式,神经网络由前馈神经元层和它们之间的连接组成,例如使用反向传播来训练。这些算法自 20 世纪 90 年代被发现以来,尽管具有迷人的特性,却一直被过度忽视。借助 Numpy、Theano 和 Tensorflow 等现代张量代数工具,这是深入竞争神经网络并实现其巨大潜力的最佳时机。在本系列的下一期中,我们将首先讨论自组织特征地图,以及如何将它们用于更多的日常数据科学应用。

本系列的下一部分,第二部分:自组织特征地图,将于 2019 年 1 月 20 日发布。看好这个空间!

星巴克数据集上的 Funk SVD 实践体验

原文:https://towardsdatascience.com/funk-svd-hands-on-experience-on-starbucks-data-set-f3e0946da014?source=collection_archive---------20-----------------------

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

Photo by Austin Distel on Unsplash

尝试使用协同过滤来个性化移动应用提供分发。

目录:

  1. 简介。
  2. 数据概述、清理和转换。
  3. 优惠在公司收入中的作用。
  4. 什么样的优惠真的让人兴奋?
  5. 使用 Funk SVD 的协同过滤。
  6. 模型微调。
  7. 结论。

介绍

如今,互联网资源、移动应用程序被设计为个性化促销优惠,以增加忠诚度,一方面超出预期,另一方面增加收入。作为 Udacity 数据科学家 Nanodegree 的一部分,我已经获得了几个可能的数据集,其中星巴克项目最让我兴奋,因为它包含模拟数据,模拟真实星巴克奖励移动应用程序中的客户行为。我很好奇以下几点是否能引起我的兴趣:

1.要约真的在公司的现金流入中扮演重要角色吗?
2。什么样的优惠真正让人兴奋,带来更多的收益?
3。如何通过使用协同过滤技术(Funk SVD)的个性化优惠分发来改善促销优惠方面的客户体验(关于 Funk SVD 的更多信息可在此处此处找到)。

数据概述、清理和转换

数据包含在三个文件中:

  • portfolio.json —包含每个报价的报价 id 和元数据。
  • profile.json —每个客户的人口统计数据。
  • transcript.json 记录交易、收到的报价、查看的报价和完成的报价。

Porftolio 仅包含 10 行描述可能的优惠和细节,如优惠持续时间、难度(完成优惠所需花费的金额)、渠道、优惠 id、优惠类型(折扣、BOGO—‘买一送一’,信息性)以及优惠完成后将获得的奖励:

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

portfolio data transformed into pandas

配置文件包含大约 17 000 名客户的数据,该数据缺少 2175 人的性别和收入值。

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

profile data transformed into pandas (first 10 rows)

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

只要对于收入,没有人报告收入为零,那么让我们用零填充 NaN 值,并检查这如何影响图表。此外,性别中缺失的值要用“S”填充,以便调查。

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

Income before (on the left side), missing values filled with zeroes on the right side

从下面的散点图中我们可以看到,缺少性别值的 2175 个消费者与缺少收入值的消费者是相同的。他们在 118 岁时形成了一个特殊的群体。因此,我们可以用“S”来填充性别中缺失的值,用零来填充收入中缺失的值,因为这不会干扰其他客户群。

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

抄本包含交易、收到的报价、查看的报价和完成的报价的记录。它还包括时间(小时)、人员 id。可以从值列中提取优惠 id、奖励和金额。

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

抄本数据集的主要挑战在于,它不包含交易和影响这些交易的报价之间的直接关联标签。为了实现这种理解,我准备了一个功能,用于验证每个客户是否收到、查看了特定的报价,交易是否发生,以及报价是否在报价有效时间内完成。

然而,一个人可能同时收到不同的报价,两个报价都可以被客户查看,如果交易发生在两个报价都被查看的时间内,那么我们应该决定哪个报价实际上导致了交易。

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

Example of the above mentioned case

由于数据集没有给出明确的方向,也没有给出背景信息,我假设在最接近交易时间查看的报价将被视为影响交易的报价。如果其中一个报价是信息性的,而第二个报价是奖励性的,那么奖励性报价应该占优势,因为我认为奖励会比不提供任何额外好处的信息性报价更吸引客户。

转换函数的代码。

在应用 trans_affected_func 函数后,我们对顾客对收到的报价的反应行为模式有了清晰的认识。

返回到开始时找到的特殊组。

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

Number of offer types received by all customers (on the left) and by special group (on the right)

比例看起来差不多。这意味着星巴克应用程序在发送优惠时没有区分这个群体。

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

Number of affected transactions within offer types for all customers (on the left) and for special group (on the right)

当谈到交易数量时,与总体人口相比,特殊群体似乎更受信息提供的影响,而不是 BOGO。对他们俩来说都是第一次打折。

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

Overall amount spent by all customers (on the left) and for special group (on the right)

特殊群体的消费金额比例不同,这可以用 BOGO 在“S”群体中不太受欢迎、交易次数较少和消费金额较少的事实来解释。有趣的是,虽然受 BOGO 影响的交易数量明显低于折扣,但对于一般客户群体来说,BOGO 和折扣的总和非常接近。

研究发现,特殊群体在行为上不同于普通消费者群体,可能需要特殊对待。

要约在公司收入中的作用

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

不受要约影响的交易产生的收入的大部分:与任何要约无关的交易产生的购买流入的 65.53% 。折现率和 BOGO 给出了彼此相对相似的数字:分别为 14.06%和 13.33%。信息性的最多 7.08%。

这 34.47%的收入如何在客户中分配?

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

受优惠影响的现金流入是由至少收到过一次优惠的近 72%的消费者产生的。在给定的客户群体中,大约 28%的人根本没有受到影响,尽管在实验过程中至少收到了一个报价。

主外卖

要约在公司收入中的作用并不重要,尽管被认为是值得注意的。

什么样的优惠真的让人兴奋?

我们可以说,如果有大量评论、完成(如果适用)和交易,宣传片真的会让人兴奋。

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

Distribution of offers and the number of affected transactions

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

Overall sum of amount spent within each offer

从上面的图表中我们可以看到,所有的报价几乎是平均分配的,同时其中一些报价的审核和完成比其他报价更频繁。10 个优惠中有 6 个具有最高的观看率,其中 2 个奖励优惠位于顶部——discount _ 10 _ 10(有效期 10 天,难度 10)和 discount_7_7(有效期 7 天,难度 7)。他们还获得了最高数量的受影响交易。

从信息报价来看,有效期为 3 天的报价是最令人兴奋的,因为它影响了大量交易(6223) —非常接近 discount_7_7 (6335)。

这三项优惠在客户消费总额方面也是冠军。

主要外卖:

发现难度分别为 7、10、持续时间分别为 7、10 的折扣优惠才是真正让人们兴奋的优惠。从信息性报价来看,持续时间为 3 天的报价对消费者的影响率也很高。这三项优惠也是在顾客总消费金额方面领先。因此,他们对消费者和星巴克都有利。

使用 Funk SVD 的协同过滤

第一步是准备用户项目矩阵,用户在索引中,报价在列中。在应用一组变换后,准备以下矩阵:

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

报价和用户被编码成整数值。单元格(I,j)中的“1”表示作为信息要约影响的结果,第 I 个用户完成了第 j 个要约或执行了交易。我们可以说第 I 个用户对第 j 个提议做出了积极的反应。零意味着第 I 个用户至少收到第 j 个报价一次,但是从未进行过被认为受报价影响的交易。行中缺少值意味着用户从未收到反映在列中的报价。

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

missing values in user item matrix

该矩阵非常稀疏,因为每列中有 63%的数据丢失。这就是我期望使用 Funk SVD 的好处,因为它可以用 1 和 0 填充所有缺失的值。

下一步是定义没有正则化的 Funk SVD 的基本形式。

然后我们可以对用户项目矩阵应用 Funk SVD。

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

Applying Funk SVD

查看矩阵分解的结果和原始用户项目矩阵。

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

Predictions and the original matrix

因此,我们有一个矩阵,其中的建议反映了对报价可能的正面(“1”)和负面/忽略反应(“0”)。

但是我们做得怎么样呢?预测够好吗?
为了验证结果,我们应该将用户项目矩阵分为训练集和测试集。在训练集上训练模型,并验证它在测试集上的表现,与简单的预测器进行比较。我们可以天真地假设所有的优惠都会被所有的客户完成/使用。请记住,我们只能验证在两个数据集中出现的客户和报价的预测。

指标和评估者

Funk SVD 函数的估计量是均方误差(MSE)。MSE 测量误差平方的平均值,即估计值和估计值之间的平均平方差。它显示了随着梯度下降迭代的增加,所有预测的平方差是如何减小的。准确性被选为绩效评估的衡量标准,因为对报价的积极和消极反应对我们来说同样重要:如果我们向客户发出报价,或者如果我们不这样做,都不会有大的伤害。这些阶层并没有失衡。考虑到所有这些,准确性度量被认为是可接受选择。

将用户项目矩阵拆分成训练和测试数据集。

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

Splitting user item matrix into train and test sets

我们可以对 5456 个普通用户进行预测,而对于 10 个用户,由于冷启动问题,我们不能进行预测,它们不会同时出现在两个集合中。

将模型拟合到训练集。

我已经在训练集上应用了该模型,并在训练集和测试集上验证了结果。此外,准确性,均方误差和 RMSE 比较了天真的预测数字。

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

MSE, RMSE, Accuracy on the train, test sets (on the left) and Naive model metrics and performance (on the right)

嗯,我的 FunkSVD 模型做得不是很好,因为该模型和朴素预测器之间的测试集的准确性差异只有 3%。

模型微调

上面的结果可以改进吗?我已经准备并应用了定制的 gridsearch_funkSVD 函数,该函数验证了模型在所有可能的参数下的表现。取得的成果反映如下。

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

注意:每当预测给出除 0 和 1 之外的值时,“过拟合”栏都填充“是”。该模型在列车上的表现证明了同样的事实。

**观察:**我们可以看到,潜在特征的数量越多,迭代次数越多,我们使用的学习越大,那么相应地,模型在训练集上过度拟合的机会就越多,相反,迭代次数、潜在特征和学习率越低,模型在测试集上的性能就越差。因此,应该在参数和性能之间进行权衡。

从上表可以看出,最佳参数是:潜在特征数=5,学习率=0.005,迭代次数=100。
这些参数给出了优化的模型。

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

结论

我分析过星巴克给定的数据,应用过数据清洗、转换和可视化。

65.53% 与要约无关的交易产生的购买流入。折扣和 bogo 给出了相对相似的数字:分别为 14.06%和 13.33%。信息性的最多 7.08%。不受报价影响的交易产生的大部分收入。

这些受优惠影响的 35% 的现金流入是由至少收到过一次优惠的 72% 的客户产生的。在给定的消费者群体中,约有 28% 根本没有受到影响,尽管在实验过程中至少收到了一份报价。

发现难度分别为 7、10、持续时间分别为 7、10 的折扣优惠才是真正让人们兴奋的优惠。从信息性报价来看,持续时间为 3 天的报价对消费者的影响率也很高。这三项优惠在客户消费总额方面领先。

基于转换后的交易信息,我形成了用户-项目-矩阵,反映了客户对收到的报价的积极或消极(忽略)反应。

由于不是所有的顾客都收到了所有可能的报价,所以选择了没有正则化的 Funk SVD 的基本形式来满足用户-项目-矩阵中缺失的值(比率)。为了评估模型的表现,我将数据分成了训练集和测试集。正如预期的那样,在测试集上,优化后的模型比天真的预测(向所有客户发送报价,好像所有客户都乐于接收和使用报价)做得更好。我们应该记住的是,对于 45 名客户,由于冷启动问题,我们无法做出预测,因为他们没有同时出现在两组中。

我无法达到超过 0.7093 的精度,因为模型在训练集上训练得越多,它就越是过度拟合。因此,在训练集上的训练模型和测试集上的预测能力之间存在权衡。尽管 0.7093 看起来并不太糟糕,但也不太有希望,我们应该考虑可能的进一步措施。

还能做什么?可能的进一步分析和改进

  1. 性能可以与监督学习算法进行比较,监督学习算法将接收客户数据作为输入,并预测消费者是否对报价做出积极响应。
  2. 可以从数据集中排除特殊人群,并且可以将模型性能与先前实现的性能进行比较。可能这个特殊的群体正在增加数据的方差,因此模型不能更好地概括。
  3. 作为我们在这里使用的离线方法的替代方法,我们可以进行在线方法,在该方法中,我们运行一个实验来确定在我们的用户群中实施一个或多个推荐系统的影响(例如,一个可以基于 Funk SVD,第二个基于监督学习算法)。对于这种情况,一个简单的实验可能是将用户随机分配到一个控制组,该组接收他们从未见过的额外优惠。然后,我们捕捉对它们的反应,并将其与所选算法的预测进行比较,并测量性能。

感谢您阅读本文。这篇博文只反映了所做分析的一小部分,更多你可以在 Github 上找到。

人工智能的未来是生物的

原文:https://towardsdatascience.com/future-of-ai-is-biological-b512d6c40fe6?source=collection_archive---------15-----------------------

自然智能是有机的/生物的。今天,我们认为人工智能(AI)是机器、机器人和软件代码。会保持不变还是发生变化成为生物神器?

迄今为止,尽管受到人脑的启发,但人工智能的旅程正在偏离它,而且越来越看起来不可持续。不可持续的能源和数据消耗,与大脑皮层和学习瓶颈相比的表现差异,为基于干细胞的神经网络提供了一个有前途的案例。

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

消耗太多能量:基于人工智能的机器可以计算得更快,但是消耗太多能量。马萨诸塞大学阿姆赫斯特分校的研究人员最近建立了人工智能训练的基准(如下图)。虽然这些型号需要 1000 瓦,但大脑需要 20W。对,就是这样。AI-1000 瓦;镍— 20W。再加上云计算的成本,云计算运行在由可再生能源和不可再生能源驱动的大型服务器群中。

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

Source: https://arxiv.org/pdf/1906.02243.pdf

**不可持续的气候变化:**如你所见,训练一个大型人工智能模型比一辆普通美国汽车排放的 5X 二氧化碳还要多。这不可持续。

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

**这是一个最小值:**专业人士不会调优一个模型,而是做 100 个或 1000 个模型,以达到最佳的人工智能模型。同样的研究人员分析了一个典型的 R & D 调优模型,发现它需要运行 1000 次。所以上面提供的数字是最小值。它乘以运行次数。

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

Source: https://arxiv.org/pdf/1906.02243.pdf

**不像大脑:**燃烧了所有的能量,我们得到了什么?狭隘的单任务问题解决者。多种人工智能模型需要通过管道连续进行复杂的对话。这与 NI 非常不同,NI 可以是串行的,也可以是大规模并行的。下次你看电影的时候,记得你是在同时听、看和想象。下面是来自脑成像研究的图像,显示了 50 个独立的过程为一项视觉运动任务并行运行。

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

fMRI… brain parallel processing Source: MIT Technology Review

**构造不同:**这是因为越来越多的大脑研究指出,大脑的构造不同于我们的深度学习架构。首先,深度学习中的数据以密集的形式表示,而大脑似乎具有稀疏的表示。这是什么意思?对于你见过的每个人,你都有一套大脑人工制品来维持他们的面孔。当你再次看到它们时,那组神经元被触发。它与基于一系列特征、轮廓等网络来识别面部的面部识别系统非常不同。AI 没有一个地方…所有的脸都混在同一个网络里。具有更稀疏表示的大脑因此可以更好地处理噪声。通过遮住你的半张脸,你不能欺骗大脑,但即使引入几个像素,人工智能也可以被欺骗,因为网络的不同部分被触发。

**不像人类一样学习:**这对于学习是如何发生的有着巨大的影响。今天的人工智能系统需要 1000 张猫的图像来识别一只猫。NI/ brain 没有。我们根据自己的知识进行推断。这意味着显著降低计算和能源。

在你童年时,父母什么时候教过你如何利用重力工作?从来没有。当你还是个婴儿的时候,你就在和它一起工作,并且在生命的早期就知道当你释放它们的时候,东西就会掉下来。NI 拥有像重力和其他物理知识的多层编码,我们可以无缝地并行连接稀疏矩阵的其他部分,从而在未知的场景中扩展我们的知识。

**边缘案例弄巧成拙:**这击中了边缘案例今天 AI 问题的核心。为了制造一辆坚固的自动驾驶汽车,它应该被训练成不会掉下悬崖。倪似乎与生俱来…我们看到重力神经元触发的悬崖,我们知道我们将会坠落。AI 需要训练。你不能把一辆车推下悬崖去训练。你永远不会有足够的数据点来训练。在这种情况下,人工智能在模拟中学习。边缘案例永远没有足够的数据,这就是为什么它们被称为边缘。这是人工智能中一个基本的自相矛盾的问题,而 NI 结构克服了这个问题。

新大脑皮层是一个复杂但可复制的结构:大脑中的 NI 结构很复杂,仍在研究中。但是在某些方面我们有着一致的看法。新皮层是大脑中与高级功能相关的部分。我们开始相信大脑的不同部分处理不同的感觉。但是越来越多的证据表明,这些不同区域的底层结构(柱子、连接等)是相似的。区别它们的是输入,而不是内在结构。用音频改变视觉区域的输入,它仍然会学习和工作。甚至每平方毫米皮层表面的神经元数量看起来也与相似。同样的组织是重复的。和 AI 很不一样。这能造出来吗?

大脑器官看起来很有希望:在实验室里,这已经在使用干细胞了。见下图的迷你大脑/ 类脑器官

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

Pea-size brain organoids at 10 months old. Credit: Muotri Lab/UCTV Source: phys.org/

它们正在培养皿中生长。该组织类似于大脑,是一个低能耗的神经网络。它们自我组织成神经组织。科学家们已经取得了长足的进步,通过提供血管系统/血管的生长来反复创造它们并稳定它们以保持更长时间。它们还被培养成具有多种功能,如视网膜细胞(原始的眼睛)引领我们进入一个传感成为可能的时代。这些类器官像人脑一样产生脑电波。最近,一个人工智能系统未能区分 9 个月大的大脑器官和早产儿大脑的电模式。

**生物计算即将到来:**这些类器官组织将在数月内自我组织和发展。可以指导他们做特定的活动吗?一系列平行的活动表明答案是肯定的。最近加州大学戴维斯分校&哈佛大学的一个团队展示了一台运行 21 种不同程序的 DNA 计算机(例如:复制、排序、识别回文和 3 的倍数等)。

可持续的低能耗、自组织、类脑学习系统越来越有可能实现。

在分子编程/生物计算、干细胞和类器官、用于特定开发的基因编辑(CRISPR)以及从这些结构中推导神经计算模型方面的进步将需要人工智能生物。

激动人心。

创新的未来将由社区自下而上推动,而不是由政府或企业自上而下推动

原文:https://towardsdatascience.com/future-of-innovation-will-be-driven-bottom-up-by-communities-not-top-down-by-governments-or-273d3650f1ec?source=collection_archive---------23-----------------------

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

一群从未谋面、位于世界不同地区(不同大陆、不同时区)的爱好者能否走到一起,合作解决社会中的大问题?

这是一个引起我兴趣的问题,我想启动 Omdena 来看看这种自下而上的模式是否可行。

这半年发生的事情很神奇。来自 63 个国家的 400 多名爱好者不仅合作应对重大的社会挑战,如战胜尼泊尔的饥饿,索马里的冲突,巴西的森林火灾,印度的性骚扰,而且他们还能够建立非常复杂的人工智能模型,同时相互帮助。更有经验的开发人员开始分享他们的工作,并帮助不太有经验的开发人员,而新的开发人员开始生成数据。

尽管面临各种挑战,但所有发烧友都有一个共同点。他们都被极大地激励着,渴望构建一个现实世界的产品来解决现实世界的问题。

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

合作而不是竞争

这是一种新的创新模式。一种模式,在这种模式下,社区可以聚集在一起解决他们的问题,共享他们的数据,并构建解决方案。

在大数据和机器学习的世界里,数据是关键。这不是复杂的算法或更好的团队,而是拥有更好(和更多)数据的团队获胜。此外,由于在线课程的泛滥,教育,特别是人工智能和人工智能等新兴技术的教育变得容易获得。现在,对于世界上任何地方的任何人来说,从像 Udemy,Coursera 这样的网站开始学习是非常容易的。通过访问数据和开源代码,人们拥有了构建自己解决方案的所有工具。

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

对社区驱动的方法感兴趣的人如何开始构建他们的解决方案?以下是步骤:

  1. 建立一个由拥有共同愿景、动机和使命的高度积极的人组成的社区。社区应该拥有多样化的经验和技能。
  2. 把他们带到一个平台下,可以懈怠。
  3. 遵循协作工作的最佳实践、流程和工具。
  4. 定期举办活动,让社区成员展示他们的工作并分享知识。
  5. 写下他们正在做的工作,以保持动力。每个人都喜欢被认可。

管理社区

社区很难管理和维护。最好的策略是用一种有共同价值观和动机的语言说话。这是建立“部落”[1]的唯一方法:一群拥有共同意义和联系的人。

另一件重要的事情是有人能把社区粘在一起。Scott Adams 在他的书《如何在几乎所有事情上都失败的情况下仍然赢得巨大成功》中谈到了建立多样化的技能,这样整体就大于部分之和。

社区外的价值

由社区构建的解决方案几乎肯定会被采用。他们知道要解决什么问题,如何获取数据,甚至知道如何构建这些解决方案。他们需要的是支持、指导和鼓励。

一个社区创造授权,建立信任,提供数据访问,产生不同的意见,并刺激创新。

但是如果创造的模型产生了经济价值会怎么样呢?这是如何分配的,特别是当一个公司把它的项目外包给社区的时候?当公司正在使用经过训练的模型时,数据和代码保留在社区中。因此,如果有经济价值,从产出中获益对社区和公司都更有意义。

公司的利益是短期的。社区的利益是长远的。

这种利用社区的方式甚至可以更大。在观看一部关于一些暴虐政权的纪录片时,我意识到暴虐政权(或公司)的目标是控制人民。这是一种经典的自上而下的方法,一些精英告诉我们其余的人什么时候做什么。

社区驱动的方法正好相反。这不是关于控制,而是关于激发自由思想。这是推动真正创新的动力,也是我认为世界应该走向的地方,我想成为其中的一部分。

当我们接触到自己的感觉和需求时,我们人类就不再是好的奴隶和下属了——非暴力沟通:马歇尔·罗森博格的一种生活语言。

模糊逻辑及其如何治愈癌症

原文:https://towardsdatascience.com/fuzzy-logic-and-how-it-is-curing-cancer-dc6bcc961ded?source=collection_archive---------19-----------------------

简要介绍软计算、模糊逻辑以及这两者如何帮助我们治愈肿瘤!

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

什么是软计算?

它是一切不脆/不实的东西。一门不能给出 100%准确答案的科学,但更合适的是它不需要 100%明确的输入。一个简单的例子是一个根据照片预测一个人年龄的程序。它研究照片中的多种特征,如头发颜色、皮肤纹理、皱纹等。并估计他们的年龄。
输入:可能不是高分辨率的照片/侧面照片。
输出:一个大概接近这个人年龄的数字。
软计算基于模糊逻辑,后面会解释。

硬计算是传统的计算方法,你们大多数人可能已经很熟悉了。一个例子是将两个数相加的程序。明确的输入和绝对的答案。

Eva Volná写的《软计算简介》是一本很棒的免费书籍,可以让你在软计算和模糊逻辑方面有一个很好的开端。

以下是本书对软计算的定义 软计算是一组方法,旨在利用对不精确性、不确定性和部分真实性的容忍来实现易处理性、健壮性和低解决方案成本。它由三种计算范式组成,即模糊 计算、神经计算概率推理。软计算的角色模型是人脑。**

什么是模糊逻辑?

传统的方法是将一杯咖啡分成两类,冷的或热的。一个温暖的接近热的杯子会属于热的类别。在模糊世界中,这个暖杯对热杯的隶属度为 70%,对冷杯的隶属度为 30%。

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

sketch made using sketchboard

模糊逻辑是一种基于真实度而不是通常的真或假的计算方法。使得对于每个元素,它对于包含在问题中的每个集合都有一个隶属度**。**

这种计算在我们的日常生活中非常有用。例如,一所学校正在为校队选拔篮球运动员,他们宣布需要身高 5 英尺 5 英寸以上的人。这会让一个 5 尺 4 的非常优秀的球员失去资格吗?还是他会被试镜因为其实差别不大?如果一个 5 英尺 3 英寸的人申请。他只比最后一个被录取的人矮一英寸。永远都是这样。这就是为什么更合适的应用程序会在身高字段中有分数,其中相对较矮的人会有较低的分数(属于高个子集的程度< 100%),而较高的人会得到满分(完全属于高个子集)。

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

seriousfacts.com

自然本身是不脆的,当你看到彩虹时,你会看到红色,然后是橙色。你盯着一个点,你可以发誓它是红色的,百分之百的红色。而且,你看到它变得越来越轻,直到它平稳地变成橙色。你说不出它到底是从哪一点停止变红,开始变成橙色的。它不是一个纯红色的笔画后面跟着一个纯橙色的笔画,相反,它是一个从 100%到 0%递减的红色百分比和从 0%到 100%递增的橙色百分比。

这和肿瘤有什么关系?

文献(以前/当前关于检测肿瘤的论文/研究)中有 3 个缺陷:

  • 没有考虑包含在肿瘤区域内的每个细胞中的癌症百分比。在大多数情况下,肿瘤由大量癌细胞组成,这些癌细胞在各个维度上逐渐消失,直到不再有癌细胞。
  • 假设肿瘤的形状是规则的几何形状(球形),其直径为 MRI 扫描中肿瘤的长度。因此,肿瘤体积的计算是错误的,因为肿瘤通常以不规则的形状存在。
  • 指定癌症级别以及治疗计划是基于依赖于肿瘤直径大小的清晰集合。意味着治疗中的剧烈改变可能基于阶段边界周围的轻微移动。

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

Photo by Damon Hall from Pexels

忽略癌变区域内癌症的不同强度(癌症分级)

在海滩上,你看不到海水以一条完美的直线结束。相反,海水的深度逐渐减少。从齐肩到齐膝,再到湿脚趾,然后是湿沙子,最后是完全干燥的沙子。对于肿瘤来说也是一样,当你检查核磁共振扫描时,你会看到一个癌块(水),癌细胞会逐渐消失,直到你到达完全健康的器官部分(沙子)。文学不能解释这一点。他们没有考虑到肿瘤固有的模糊性。一般来说,生物系统,尤其是癌性肿瘤,并不显示出清晰的边界。因此,为了精确描述肿瘤,必须考虑内在分级,以评估初始肿瘤结构和追踪治疗进展。一些细胞可能仅包含 40%癌组织的模糊部分将被认为是 100%的癌细胞。这高估了病人的情况,并可能导致不必要的沉重的愈合过程。

假设肿瘤是规则的球形,这种情况很少发生

癌症检测文献中的另一个大错误是,他们假设肿瘤是规则的球形,具有肿瘤本身中最长线的直径。这可能导致非常错误的尺寸,特别是在肿瘤形状不规则或者最长和最短尺寸之间的差异很大的情况下。通常基于最长直径的体积估计导致比实际情况大得多的体积大小。这种测量技术只能对完美或接近完美的球形病变精确。

主要基于肿瘤直径长度来定义癌症的明确阶段

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

source: wikimedia commons

癌症分期用于描述和排列癌症的严重程度。从阶段 0 到阶段 4。其中 IV 是最严重的。第四阶段意味着疾病已经扩散到其他器官。自从洛菲·扎德提出模糊集合论以来,人们就认识到了生物系统的模糊性,从那以后,生物现象的层次性得到了广泛的重视和应用。例如,如果一个阶段落在分别作为下限和上限的肿瘤直径 2 cm 和 4 cm 的值之间,那么值 1.99 和 4.01 呢?对于这样一个微小的偏离边界的情况,我们应该把整个治疗计划或药物设计从一个阶段转移到另一个阶段吗?如果我们不这样做,那么我们应该在什么指数值设置限制?这是一个典型的悖论的例子,模糊集合论就是为此而发明的。

我在这里写的很多东西,都是从这篇智能癌症分期的肿瘤体积模糊化论文中学到的。

尺度模糊匹配

原文:https://towardsdatascience.com/fuzzy-matching-at-scale-84f2bfd0c536?source=collection_archive---------2-----------------------

从 3.7 小时到 0.2 秒。如何以一种甚至可以扩展到最大数据集的方式执行智能字符串匹配。

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

Same but different. Fuzzy matching of data is an essential first-step for a huge range of data science workflows.

# # # 2020 年 12 月更新:一种更快、更简单的模糊匹配方法现已包含在本文末尾,并提供了在任何数据集上实现该方法的完整代码###

D 现实世界中的 ata 是杂乱无章的。处理乱七八糟的数据集是痛苦的,耗费了大量时间,而这些时间本可以用来分析数据本身。

本文重点关注“模糊”匹配,以及它如何通过以下方式帮助自动化大量数据科学工作流中的重大挑战:

  1. **重复数据删除。**对齐数据集中相似的类别或实体(例如,我们可能需要将“D J Trump”、“D. Trump”和“Donald Trump”组合成同一个实体)。
  2. **记录联动。**连接特定实体上的数据集(例如,将‘D . J . Trump’的记录连接到他的维基百科页面的 URL)。

通过使用从自然语言处理领域借用的新方法,我们可以在大型数据集上执行这两项任务。

大数据的模糊匹配问题

有许多算法可以提供模糊匹配(参见这里如何用 Python 实现),但是当用于超过几千条记录的适度数据集时,它们很快就失效了。

这样做的原因是,他们将每个记录与数据集中的所有其他记录进行比较。在计算机科学中,这被称为二次时间,在处理较大的数据集时会很快形成障碍:

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

Number of records on the left against the number of operations required for an algorithm that works in quadratic time. A relativity small data set of 10k records would require 100m operations.

更糟糕的是,大多数字符串匹配函数还依赖于被比较的两个字符串的长度,因此在比较长文本时速度会更慢。

众所周知的 NLP 算法如何帮助解决这个问题

这个问题的解决方案来自一个众所周知的 NLP 算法。词频,逆文档频率(或 tf-idf)从 1972 年开始用于语言问题。

这是一个简单的算法,它将文本分成“块”(或 ngrams),对给定样本的每个块的出现次数进行计数,然后根据该块在数据集的所有样本中的稀有程度对其进行加权。这意味着有用的单词从文本中出现的更常见单词的“噪音”中过滤出来。

虽然这些组块通常应用于整个单词,但是没有理由为什么相同的技术不能应用于单词中的字符集。例如,我们可以将每个单词分成 3 个字符的 ngrams,对于单词“Department ”,将输出:

' De', 'Dep', 'epa', 'par', 'art', 'rtm', 'tme', 'men', 'ent', 'nt '

然后,我们可以在代表我们的数据集的项目矩阵中比较这些块,以寻找接近的匹配。这种寻找接近匹配的方法应该非常有效,并且通过其对数据中不太常见的字符组给予更大重视的能力,还可以产生高质量的匹配。让我们来测试一下吧!

真实世界的例子

我们将用来测试该算法的例子是一组在 Contracts Finder 上发布合同的英国公共组织。这些组织的名称非常混乱,看起来好像是通过自由文本字段输入系统的。

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

A sample of the data set — there are 3,651 different buying organisations in total

智能重复数据删除

我们将首先探讨如何对相近的匹配项进行重复数据删除。使用 Python 的 Scikit-Learn 库可以使这个过程变得轻松:

  1. 创建一个函数将字符串分割成字符。
  2. 使用 Scikit-Learn 从这些字符创建一个 tf-idf 矩阵。
  3. 使用余弦相似度来显示总体中最接近的匹配。

ngram 功能

below 函数既用作文本数据的清理函数,也用作将文本拆分成 ngrams 的方法。代码中添加了注释以显示每一行的用途:

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

The function used to turn a string into a series of ngrams for matching on

应用函数并创建 tf-idf 矩阵

Scikit 中 tf-idf 实现的伟大之处在于它允许向其中添加自定义函数。因此,我们可以添加上面创建的函数,只用几行代码就可以构建矩阵:

from sklearn.feature_extraction.text import TfidfVectorizerorg_names = names['buyer'].unique()
vectorizer = TfidfVectorizer(min_df=1, analyzer=ngrams)
tf_idf_matrix = vectorizer.fit_transform(org_names)

通过余弦相似度找到接近的匹配

您可以在这里使用 Scikit 中的余弦相似性函数,但是这并不是查找接近匹配的最有效的方法,因为它会返回每个样本的数据集中每个项目的接近度分数。相反,我们将使用一个更快的实现,可以在这里找到:

import numpy as np
from scipy.sparse import csr_matrix
!pip install sparse_dot_topn 
import sparse_dot_topn.sparse_dot_topn as ctdef awesome_cossim_top(A, B, ntop, lower_bound=0):
    # force A and B as a CSR matrix.
    # If they have already been CSR, there is no overhead
    A = A.tocsr()
    B = B.tocsr()
    M, _ = A.shape
    _, N = B.shape

    idx_dtype = np.int32

    nnz_max = M*ntop

    indptr = np.zeros(M+1, dtype=idx_dtype)
    indices = np.zeros(nnz_max, dtype=idx_dtype)
    data = np.zeros(nnz_max, dtype=A.dtype)ct.sparse_dot_topn(
        M, N, np.asarray(A.indptr, dtype=idx_dtype),
        np.asarray(A.indices, dtype=idx_dtype),
        A.data,
        np.asarray(B.indptr, dtype=idx_dtype),
        np.asarray(B.indices, dtype=idx_dtype),
        B.data,
        ntop,
        lower_bound,
        indptr, indices, data)return csr_matrix((data,indices,indptr),shape=(M,N))

将所有这些放在一起,我们得到以下结果:

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

Witchcraft! The algorithm is eerily good at identifying duplicate records.

非常令人印象深刻,但它有多快?让我们比较一下使用“fuzzywuzzy”库计算近似匹配的更传统的方法:

!pip install fuzzywuzzy
from fuzzywuzzy import fuzz
from fuzzywuzzy import processt1 = time.time()
print(process.extractOne('Ministry of Justice', org_names)) #org names is our list of organisation names
t = time.time()-t1
print("SELFTIMED:", t)
print("Estimated hours to complete for full dataset:", (t*len(org_names))/60/60)**Outputs:**
SELFTIMED: 3.7s 
Estimated hrs to complete for full dataset: 3.78hrs

使用传统方法的基线时间大约是 3.7 小时。我们的算法用了多长时间来发挥它的魔力?

import time
t1 = time.time()
matches = awesome_cossim_top(tf_idf_matrix, tf_idf_matrix.transpose(), 10, 0.85)
t = time.time()-t1
print("SELFTIMED:", t)**Outputs:
**  SELFTIMED: 0.19s

哇——我们已经将预计时间从 3.7 小时减少到大约五分之一秒(大约 66,000 倍的速度提升!)

记录链接和不同的方法

如果我们想使用这种技术来匹配另一个数据源,那么我们可以回收大部分代码。在下面的部分,我们将看到这是如何实现的,并使用 K 最近邻算法作为一种替代的接近度测量。

我们想要加入的数据集是由英国国家统计局(ONS)创建的一组“干净”的组织名称:

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

The clean data set we would like to join against.

如下面的代码所示,这种方法的唯一区别是使用 tdif 矩阵来转换杂乱的数据集,该矩阵是在干净的数据集上学习的。

“getNearestN”然后使用 Scikit 的 K 最近邻实现来查找数据集中最接近的匹配:

from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer
import reclean_org_names = pd.read_excel('Gov Orgs ONS.xlsx')
clean_org_names = clean_org_names.iloc[:, 0:6]org_name_clean = clean_org_names['Institutions'].unique()print('Vecorizing the data - this could take a few minutes for large datasets...')
vectorizer = TfidfVectorizer(min_df=1, analyzer=ngrams, lowercase=False)
tfidf = vectorizer.fit_transform(org_name_clean)
print('Vecorizing completed...')from sklearn.neighbors import NearestNeighbors
nbrs = NearestNeighbors(n_neighbors=1, n_jobs=-1).fit(tfidf)org_column = 'buyer' #column to match against in the messy data
unique_org = set(names[org_column].values) # set used for increased performance###matching query:
def getNearestN(query):
  queryTFIDF_ = vectorizer.transform(query)
  distances, indices = nbrs.kneighbors(queryTFIDF_)
  return distances, indicesimport time
t1 = time.time()
print('getting nearest n...')
distances, indices = getNearestN(unique_org)
t = time.time()-t1
print("COMPLETED IN:", t)unique_org = list(unique_org) #need to convert back to a list
print('finding matches...')
matches = []
for i,j in enumerate(indices):
  temp = [round(distances[i][0],2), clean_org_names.values[j][0][0],unique_org[i]]
  matches.append(temp)print('Building data frame...')  
matches = pd.DataFrame(matches, columns=['Match confidence (lower is better)','Matched name','Origional name'])
print('Done')

这会产生以下结果:

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

Not all items exist across the data sets. Thankfully the closeness score can be used to apply a threshold to what counts as a positive match.

从这个例子中可以看出,并不是所有的项目都出现在两个数据集中,但是 K 个最近邻仍然可以找到最接近的匹配。因此,我们需要对接近度分数应用一个阈值来确定什么算匹配。

使用这种方法,将 3,651 个实体与我们的干净数据集(包含 3,000 个实体)匹配不到一秒钟。

2020 年 12 月更新,这是一种使用 NMSLIB 的更快、更可扩展的新方法

虽然我们在本帖中构建了自己的函数来寻找向量间的近似匹配,但还有许多库存在,它们的唯一目的是加速这一过程。

不幸的是,大部分这些要求向量是密集的,并且不能处理我们在这篇文章中创建的大型稀疏(大部分是空的)矩阵。幸运的是,有一个库可以很好地处理稀疏矩阵;NMSLIB。我已经在这里更详细地写了这个库,但是本质上,NMSLIB 可以在一个矩阵上创建一个索引,并在给定的输入上执行极快的查询。在任何数据集上实现这一点的完整、有文档记录的代码可以在下面找到:

[## 模糊 _ 匹配 _ 更新. ipynb

合作笔记本

drive.google.com](https://drive.google.com/file/d/1Z4-cEabpx7HM1pOi49Mdwv7WBOBhn2cl/view?usp=sharing)

**总之,**在处理大量记录时,tf-idf 是一种高效、高性能的数据清理、重复数据删除和匹配方法。

代码,参考资料及进一步阅读:

请参见下面的 colab 文档,查看这篇文章的完整代码:

[## 快速匹配

快速模糊匹配

colab.research.google.com](https://colab.research.google.com/drive/1qhBwDRitrgapNhyaHGxCW8uKK5SWJblW)

这篇文章的灵感来自范登博客上的以下帖子:

[## Python 中的超快速字符串匹配

传统的字符串匹配方法,如 Jaro-Winkler 或 Levenshtein 距离度量,对于…

bergvca.github.io](https://bergvca.github.io/2017/10/14/super-fast-string-matching.html)

关于 tf-idf 的更多信息可以在下面的文章中找到

[## 如何在 Python 中使用 TF-IDF 处理文本数据

计算机擅长处理数字,但不太擅长处理文本数据。最广泛使用的处理技术之一…

medium.com](https://medium.com/free-code-camp/how-to-process-textual-data-using-tf-idf-in-python-cd2bbc0a94a3)

基于 Levenshtein 和 PostgreSQL 的模糊匹配

原文:https://towardsdatascience.com/fuzzy-matching-with-levenshtein-and-postgresql-ed66cad01c03?source=collection_archive---------8-----------------------

构建容错搜索引擎

用户体验是多种因素综合的结果:UI 设计、响应时间、对用户错误的容忍度等。这篇文章的范围属于第三类。更具体地说,我将解释如何使用 PostgreSQL 和 Levenshtein 的距离算法来处理搜索表单中的用户错误。例如,我们可能在我们的站点中查找一个名为“Markus”的用户,尽管我们可能以“Marcus”的名称搜索他(这两个名称都存在,并且在不同的语言中是等价的)。

让我们从熟悉 Levenshtein 的距离算法开始。

Levenshtein 距离算法

Levenshtein 的距离(为了简洁起见,我们从现在开始称之为 LD)旨在计算两个字符串之间的不相似性(因此,值越高,它们越不相似)。此度量表示需要应用于其中一个字符串以使其等于另一个字符串的操作数量。这些操作包括:插入字符、删除字符或替换字符。值得一提的是,每个操作都有一个相关的成本,尽管通常将所有操作的成本都设置为 1。

你很少需要实现 LD 算法,因为它已经存在于许多主流编程语言的库中。然而,理解它是如何工作的是值得的。我们将通过一个示例来确定“Marcus”和“Markus”之间的 LD,将所有操作的等价成本设置为 1:

  1. 我们创建一个 m×n 维的矩阵 D,其中 m 和 n 代表我们的两个字符串的长度(向前称为 A 和 B)。
  2. 我们从上到下从左到右迭代矩阵,计算相应子串之间的 LD。
  3. 我们计算增加的成本(ac),如果 A[i]等于 B[j]则为 0,否则为 1。
  4. 我们执行以下操作:

D[i,j] = min(D[i-1,j] + 1,D[i,j-1] + 1,D[i-1,j-1] + ac)

这意味着单元(I,j)具有由下式中的最小值确定的 LD:

  • 正上方的单元格加 1(由于插入操作)。
  • 左边的单元格加 1(由于插入操作)。
  • 左上方的单元格加上 ac(由于替换操作)。

以下矩阵代表了这一过程的最终结果:

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

LD matrix of “Marcus” VS “Markus”

上面的矩阵不仅提供了完整字符串之间的 LD,还提供了子字符串任意组合之间的 LD。完整字符串之间的 LD 可以在位置(m,n)找到。在这种情况下,我们可以看到“马库斯”和“马库斯”的 LD 为 1(红色),这是由“k”替换“c”造成的。

在 PostgreSQL 中使用 Levenshtein 距离

现在你已经了解了算法,是时候进入实用部分了。使用 PostgreSQL 应用 LD 算法非常简单,这都要归功于 fuzzystrmatch 扩展。这个扩展为模糊字符串匹配提供了不同的算法。可用的选项有 LD 算法和一组语音函数。请注意,这些语音函数(Soundex、Metaphone 和 Double Metaphone)对于非英语字符串可能无法发挥最佳性能。因此,我认为 LD 是国际申请中最可靠的选择。

不过,让我们继续将这个扩展添加到我们的 PostgreSQL 数据库中:

CREATE EXTENSION fuzzystrmatch;

此时,计算 LD 就像运行以下查询一样简单:

SELECT levenshtein(str1, str2);

例如:

SELECT levenshtein('Marcus', 'Markus');

因此,如果您有一个用户表,并且想要查找与用户输入具有相似名称的所有用户(例如,设置最大 LD 为 1),您的查询可能如下所示:

SELECT name FROM user WHERE levenshtein('Marcus', name) <= 1;

在这种情况下,“Marcus”代表用户输入。在这个查询中,我们将搜索名为 Marcus 或者名称的 LD 为 1 的用户(例如,“Markus”)。

请注意,fuzzystrmatch 提供了另一个有趣的函数,在这种情况下会更有效一些:levenshtein_less_equal。该函数允许您定义一个最大距离阈值,超过该阈值后 PostgreSQL 将停止计算。换句话说,如果我们将最大阈值设置为 1,并且我们的字符串对的 ld 为 6,则 fuzzystrmatch 将在达到 LD 为 2 后停止计算,因为无论如何,我们对这样的字符串对的 LD 不感兴趣,从而节省一些计算资源(并产生 LD 为 2)。最后,还值得注意的是,这两个函数都有一个版本,允许您定义每个操作(插入、删除和替换)的成本。

FuzzyWuzzy:如何在 Python 上测量字符串距离

原文:https://towardsdatascience.com/fuzzywuzzy-how-to-measure-string-distance-on-python-4e8852d7c18f?source=collection_archive---------14-----------------------

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

Some fuzzy plant leaves. Source: Pixabay.

Python 的 FuzzyWuzzy 库用于测量两个字符串之间的相似性。以下是你也可以开始使用它的方法。

有时候,我们需要看两个字符串是否相同。当将输入的密码与你的登录数据库中存储的密码进行比较时,“相似性”是不够的。

然而,其他时候,事情会变得有点… 模糊

如果我的顾客的名字是艾伯特·汤普森,但是他用一张名为艾伯特·g·汤普森的信用卡付款,我应该报警举报欺诈吗?《魔戒 2:双塔》和《魔戒 2:双塔》是否应该被一个网站当做两部完全独立的书?奥地利和澳大利亚真的是两个不同的国家吗?

好吧,我可能被最后一个问题冲昏了头脑,但是你明白了。

字符串距离度量

我们想要的是一个测量两个字符串有多相似的函数,但是对小的变化是健壮的。这个问题听起来很普通:科学家们已经想出了很长时间的解决方案。

雅克卡距离:第一种方法

最直观的一个就是 Jaccard 距离。它可以推广到任意两个集合的距离度量。它通过以下公式进行测量:

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

也就是说,有多少个元素在任一集合上,但不被两者共享,除以不同元素的总数。

例如,给定字符串“Albert”和“Alberto”,它将报告 85.7%的相似性,因为它们共享总共 7 个字母中的 6 个。

然而,这不是一个专门为字符串定制的措施。

它将在许多用例中失败,因为它没有真正考虑排序。例如,两个变位词,如“铁路安全”和“童话”,总是 100%匹配,即使这些字符串完全不同。

莱文斯坦距离

俄罗斯科学家 Vladimir Levenshtein 在 60 年代发明了这种方法,这种方法更加直观:它计算需要多少次替换,给定一个字符串 u ,将其转换为 v

对于这种方法,替代被定义为:

  • 擦除字符。
  • 添加一个。
  • 用一个字符替换另一个字符。

需要对 u 进行的这些操作的最小数量,以便将其转换为 *v,*对应于这两个串之间的 Levenshtein 距离。

它可以通过以下公式递归获得:

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

其中 ij 是我们将要比较的子串的最后一个字符的索引。如果这些字符不同,最后一个表达式中的第二项等于 1,如果相同,则等于 0。

这是 Python 的 FuzzyWuzzy 库使用的度量。

在 Python 中使用 FuzzyWuzzy

要获得两个字符串之间的相似率,我们要做的就是:

from fuzzywuzzy import fuzzsimilarity = fuzz.ratio("hello","world")

你可能注意到了我说的比率。ratio 方法将总是返回一个介于 0 和 100 之间的数字(是的,我更希望它介于 0 和 1 之间,或者称之为百分比,但是各有各的)。

可以证明,Levenshtein 距离至多是最长字符串的长度:用较长字符串的第一部分替换较短字符串中的所有字符,然后添加其余的字符。

这就是我们如何将距离归一化以返回一个比率,以便在给定不同大小的输入的情况下,该数字不会大幅波动。

这解决了前面提到的一些问题:

fuzz.ratio("Albert Thompson", "Albert G. Thompson") #91%fuzz.ratio("The Lord of the Rings II: The Two Towers",
           "The Lord of the Rings 2: the 2 Towers") #88%

即使这可能会带来一些新的问题:

#88% for two different countries
fuzz.ratio("Austria","Australia")#57% but it's the same country
fuzz.ratio("Czechia","Czech Republic")

其他模糊不清的方法

Python 的 FuzzyWuzzy 库不仅为我们提供了普通的 Levenshtein 距离,还提供了一些我们可以利用的其他方法。

部分比率

partial_ratio 方法用较短字符串的长度计算较长字符串的所有子字符串的 FuzzyWuzzy 比率,然后返回最高匹配。

举个例子,

fuzz.partial_ratio("abc","a") == 
      min([fuzz.ratio( char, "a") for char in "abc"])

这有一些有趣的影响:

fuzz.partial_ratio("Thomas and His Friends", "Thomas") #100%fuzz.partial_ratio("Batman vs Superman", "Batman") #100%

实际上,partial_ratio 方法可以是对【T2 包含】字符串方法的模糊替换,就像常规比率可以替换【T4 等于】方法一样。

但是,对于相似但单词出现顺序不同的字符串,它将失败。即使是轻微的顺序变化也会破坏它。

#72% with basically the same idea
fuzz.partial_ratio("Peanut Butter and Jelly", 
                   "Jelly and Peanut Butter")

#86% with a random (carefully selected) string
fuzz.partial_ratio("Peanut Butter and Jelly", "Otter and Hell")

令牌 _ 排序 _ 比率

Token Sort Ratio 将两个字符串分成单词,然后在对它们调用常规 Ratio 之前,再次按字母数字顺序将它们连接起来。

这意味着:

fuzz.partial_ratio("Batman vs Superman", "Superman vs Batman") #100%fuzz.partial_ratio("a b c", "c b a") #100%

令牌 _ 集合 _ 比率

记号集比率将每个字符串分成单词,将两个列表都变成集合(丢弃重复的单词),然后在进行比率之前对它们进行排序。

这样,我们不仅排除了共享单词,还考虑到了重复。

fuzz.token_set_ratio("fun","fun fun fun") #100%fuzz.token_set_ratio("Lord the Rings of", "Lord of the Rings") #100%

结论

Python 的 FuzzyWuzzy 库是一个非常有用的工具。无论是对于客户的姓名匹配,还是充当穷人的单词嵌入,都可以省去你很多麻烦,或者对你的机器学习模型的特征工程有帮助。

然而,由于它需要预处理(比如将两个字符串都转换成小写),并且不考虑同义词,所以对于可能需要实际 NLP 或聚类方法的情况,它可能不是最佳解决方案。

我希望这篇文章对你有所帮助,如果你在工作中发现 FuzzyWuzzy 的其他用途,请告诉我!

关注我的TwitterMedium了解更多 Python 教程、技巧和窍门。

你可以在我的 个人网站 中看到我正在做的事情以及我最近的文章和笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值