All posts by dotte

深度解析JavaScript的this关键字

这篇文章通过简单的术语和一个真实的例子解释了 this 是什么以及为什么说它很有用。

你的 this

我发现,很多教程在解释 JavaScript 的 this 时,通常会假设你拥有 Java、C++ 或 Python 等面向对象编程语言的背景。这篇文章主要面向那些对 this 没有先入之见的人。我将尝试解释什么是 this 以及为什么它很有用。

或许你迟迟不肯深入探究 this,因为它看起来很奇怪,让你心生畏惧。你之所以使用它,有可能仅仅是因为 StackOverflow 说你需要在 React 用它来完成一些事情。

在我们深入了解它的真正含义以及为什么要使用它之前,我们首先需要了解函数式编程和面向对象编程之间的区别。

函数式编程与面向对象编程

你可能知道也可能不知道,JavaScript 具有函数和面向对象的构造,你可以选择关注其中一个或两者兼而有之。

在我的 JavaScript 之旅的早期,我一方面拥抱函数式编程,一方面像避免瘟疫一样排斥面向对象编程。我对面向对象关键字 this 不甚了解。其中的一个原因是我不明白它存在的必要性。在我看来,完全可以不依赖 this 就可以完成所有的事情。

在某种程度上,我的看法是对的。

你可能只关注其中一种范式而从来不去了解另外一种,作为一名 JavaScript 开发者,你的局限性就体现在这里。为了说明函数式编程和面向对象编程之间的差别,我将使用一组 Facebook 好友数据作为示例。

假设你正在构建一个用户登录 Facebook 的 Web 应用,在登录后显示一些 Facebook 好友的数据。你需要访问 Facebook 端点来获取好友的数据,可能包含一些信息,例如 firstName、lastName、username、numFriends、friendData、birthday 和 lastTenPosts。

const data = [
  {
    firstName: 'Bob',
    lastName: 'Ross',
    username: 'bob.ross',    
    numFriends: 125,
    birthday: '2/23/1985',
    lastTenPosts: ['What a nice day', 'I love Kanye West', ...],
  },
  ...
]

你从(臆造的)Facebook API 获得上面的数据。现在,你需要转换它们,让它们符合项目需要的格式。假设你要为每个用户的朋友显示以下内容:

  • 它们的名字,格式为$ {firstName} $ {lastName}
  • 三篇随机的帖子;
  • 从他们生日起到现在的天数。
函数式方法

如果使用函数式方法,就是将整个数组或数组的每个元素传给一个返回所需操作数据的函数:

const fullNames = getFullNames(data)
// ['Ross, Bob', 'Smith, Joanna', ...]

你从原始数据开始(来自 Facebook API),为了将它们转换为对你有用的数据,你将数据传给一个函数,这个函数将输出你可以在应用程序中显示给用户的数据。

你也可以通过类似的方式获取三个随机帖子并计算朋友生日至今的天数。

函数式方法就是指接受原始数据,将数据传给一个或多个函数,并输出对你有用的数据。

面向对象方法

对于那些刚接触编程和学习 JavaScript 的人来说,面向对象方法可能会更难掌握。面向对象是指你将每个朋友转换为对象,对象包含了用于生成你所需内容的一切。

你可以创建包含 fullName 属性的对象,以及 getThreeRandomPosts 和 getDaysUntilBirthday 函数。

function initializeFriend(data) {
  return {
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from data.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use data.birthday to get the num days until birthday
    }
  };
}
const objectFriends = data.map(initializeFriend)
objectFriends[0].getThreeRandomPosts() 
// Gets three of Bob Ross's posts

面向对象方法是为你的数据创建对象,这些对象包含了状态和用于生成对你和你的项目有用的数据的信息。

这与 this 有什么关系?

你可能没有想过会写出类似 initializeFriend 这样的东西,你可能会认为它很有用。你可能还会注意到,它其实并非真正的面向对象。

getThreeRandomPosts 或 getDaysUntilBirthday 方法之所以有用,主要是因为闭包。因为使用了闭包,所以在 initializeFriend 返回之后,它们仍然可以访问 data。

假设你写了另一个方法,叫 greeting。请注意,在 JavaScript 中,方法只是对象的一个属性,这个属性的值是一个函数。我们希望 greeting 可以做这些事情:

function initializeFriend(data) {
  return {
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from data.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use data.birthday to get the num days until birthday
    },
    greeting: function() {
      return `Hello, this is ${fullName}'s data!`
    }
  };
}

这样可以吗?

不行!

新创建对象的所有东西都可以访问 initializeFriend 的变量,但对象本身的属性或方法不行。当然,你可能会问:

难道你不能用 data.firstName 和 data.lastName 来返回 greeting 吗?

当然可以。但如果我们还想在 greeting 中包含朋友生日至今的天数,该怎么办?我们必须以某种方式从 greeting 中调用 getDaysUntilBirthday 方法。

是时候让 this 上场了!

那么,this 是什么

在不同的情况下,this 代表的东西也不一样。默认情况下,this 指向全局对象(在浏览器中,就是 window 对象)。但光知道这点对我们并没有太大帮助,对我来说有用的是 this 的这条规则:

如果 this 被用在一个对象的方法中,并且这个方法在对象的上下文中调用,那么 this 就指向这个对象本身。

你会问:“在对象的上下文中调用……这又是什么意思”?

别担心,稍后我们会解释这个。

因此,如果我们想在 greeting 中调用 getDaysUntilBirthday,可以直接调用 this.getDaysUntilBirthday,因为在这种情况下,this 指向对象本身。

注意:不要在全局作用域或在另一个函数作用域内的常规 ole 函数中使用 this!this 是一个面向对象的构造。因此,它只在对象(或类)的上下文中有意义!

让我们重构 initializeFriend,让它使用 this:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const numDays = this.getDaysUntilBirthday()      
      return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
    }
  };
}

现在,在执行完 intializeFriend 后,这个对象的所有东西都限定在对象本身。我们的方法不再依赖于闭包,它们将使用对象本身包含的信息。

这是 this 的一种使用方式,现在回到之前的问题:为什么说 this 因上下文不同而不同?

有时候,你希望 this 可以指向不一样的东西,比如事件处理程序就是一个很好的例子。假设我们想在用户点击链接时打开朋友的 Facebook 页面。我们可能会在对象中添加一个 onClick 方法:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const numDays = this.getDaysUntilBirthday()      
      return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
    },
    onFriendClick: function() {
      window.open(`https://facebook.com/${this.username}`)
    }
  };
}

请注意,我们向对象添加了 username,让 onFriendClick 可以访问它,这样我们就可以在新窗口中打开朋友的 Facebook 页面。现在编写 HTML:

<button id="Bob_Ross">
  <!-- A bunch of info associated with Bob Ross -->
</button> 

然后是 JavaScript:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById('Bob_Ross')
bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)

在上面的代码中,我们为 Bob Ross 创建了一个对象。我们获得与 Bob Ross 相关的 DOM 元素。现在我们想要调用 onFriendClick 方法来打开 Bob 的 Facebook 页面。应该没问题吧?

不行!

什么地方出了问题?

请注意,我们为 onclick 处理程序选择的函数是 bobRossObj.onFriendClick。看到问题所在了吗?如果我们像这样重写它:

bobRossDOMEl.addEventListener("onclick", function() {
  window.open(`https://facebook.com/${this.username}`)
})

现在你看到问题所在了吗?当我们将 onclick 处理程序指定为 bobRossObj.onFriendClick 时,我们实际上是将 bobRossObj.onFriendClick 的函数作为参数传给了处理程序。它不再“属于”bobRossObj,也就是说 this 不再指向 bobRossObj。这个时候 this 实际上指向的是全局对象,所以 this.username 是 undefined 的。

是时候让 bind 上场了!

显式绑定 this

我们需要做的是将 this 显式绑定到 bobRossObj。我们可以使用 bind 来实现:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById('Bob_Ross')
bobRossObj.onFriendClick = bobRossObj.onFriendClick.bind(bobRossObj)
bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)

之前,this 是基于默认规则设置的。通过使用 bind,我们在 bobRossObj.onFriendClick 中将 this 的值显式设置为对象本身,也就是 bobRossObj。

到目前为止,我们已经知道为什么 this 很有用以及为什么有时候需要显式绑定 this。接下来我们要讨论的最后一个主题是箭头函数。

箭头函数

你可能已经注意到,箭头函数像是一个时髦的新事物。人们似乎很喜欢它们,因为它们简洁而优雅。你可能已经知道它们与一般函数略有不同,但不一定非常清楚这些区别究竟是什么。

或许箭头函数的不同之处在于:

在箭头函数内部,无论 this 处于什么位置,它指的都是相同的东西。

让我们用 initializeFriend 示例解释一下。假设我们想在 greeting 中添加一个辅助函数:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      function getLastPost() {
        return this.lastTenPosts[0]
      }
      const lastPost = getLastPost()           
      return `Hello, this is ${this.fullName}'s data!
             ${this.fullName}'s last post was ${lastPost}.`
    },
    onFriendClick: function() {
      window.open(`https://facebook.com/${this.username}`)
    }
  };
}

这样可以吗?如果不行,要怎样修改才行?

这样当然是不行的。因为 getLastPost 不是在对象的上下文中调用的,所以 getLastPost 中的 this 会回退到默认规则,即指向全局对象。

“在对象的上下文中调用”可能是一个比较含糊的概念。要确定一个函数是否是在“对象的上下文中”被调用,最好的办法是看一下函数是如何被调用的,以及是否有对象“附加”在函数上。

让我们来看看执行 bobRossObj.onFriendClick() 时会发生什么:“找到 bobRossObj 对象的 onFriendClick 属性,调用分配给这个属性的函数”。

再让我们来看看执行 getLastPost() 时会发生什么:”调用一个叫作 getLastPost 的函数”。有没有注意到,这里并没有提及任何对象?

现在来测试一下。假设有一个叫作 functionCaller 的函数,它所做的事情就是调用其他函数:

functionCaller(fn) {
  fn()
}

如果我们这样做会怎样:functionCaller(bobRossObj.onFriendClick)?可不可以说 onFriendClick 是“在对象的上下文中”被调用的?this.username 的定义存在吗?

让我们来看一下:“找到 bobRossObj 对象的 onFriendClick 属性。找到这个属性的值(恰好是一个函数),将它传给 functionCaller,并命名为 fn。现在,执行名为 fn 的函数”。请注意,函数在被调用之前已经从 bobRossObj 对象中“分离”,因此不是“在对象 bobRossObj 的上下文中”调用,所以 this.username 是 undefined 的。

让箭头函数来救场:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const getLastPost = () => {
        return this.lastTenPosts[0]
      }
      const lastPost = getLastPost()           
      return `Hello, this is ${this.fullName}'s data!
             ${this.fullName}'s last post was ${lastPost}.`
    },
    onFriendClick: function() {
      window.open(`https://facebook.com/${this.username}`)
    }
  };
}

箭头函数是在 greeting 中声明的。我们知道,当我们在 greeting 中使用 this 时,它指向对象本身。因此,箭头函数中的 this 指向的对象就是我们想要的。

英文原文:

https://medium.freecodecamp.org/a-deep-dive-into-this-in-javascript-why-its-critical-to-writing-good-code-7dca7eb489e7

初始GAN

要说最近几年在深度学习领域最火的莫过于生成对抗网络,即 Generative Adversarial Networks(GANs)了。它是 Ian Goodfellow 在 2014 年发表的,也是这四年来出现的各种 GAN 的变种的开山鼻祖了,下图表示这四年来有关 GAN 的论文的每个月发表数量,可以看出在 2014 年提出后到 2016 年相关的论文是比较少的,但是从 2016 年,或者是 2017 年到今年这两年的时间,相关的论文是真的呈现井喷式增长。

那么,GAN 究竟是什么呢,它为何会成为这几年这么火的一个研究领域呢?

GAN,即生成对抗网络,是一个生成模型,也是半监督和无监督学习模型,它可以在不需要大量标注数据的情况下学习深度表征。最大的特点就是提出了一种让两个深度网络对抗训练的方法。

目前机器学习按照数据集是否有标签可以分为三种,监督学习、半监督学习和无监督学习,发展最成熟,效果最好的目前还是监督学习的方法,但是在数据集数量要求更多更大的情况下,获取标签的成本也更加昂贵了,因此越来越多的研究人员都希望能够在无监督学习方面有更好的发展,而 GAN 的出现,一来它是不太需要很多标注数据,甚至可以不需要标签,二来它可以做到很多事情,目前对它的应用包括图像合成、图像编辑、风格迁移、图像超分辨率以及图像转换等。

比如图片的转换, pix2pixGAN(https://github.com/affinelayer/pix2pix-tensorflow) 就可以做到,其结果如下图所示,分割图变成真实照片,从黑白图变成彩色图,从线条画变成富含纹理、阴影和光泽的图等等,这些都是这个 pix2pixGAN 实现的结果。

CycleGAN(https://github.com/junyanz/CycleGAN) 则可以做到风格迁移,其实现结果如下图所示,真实照片变成印象画,普通的马和斑马的互换,季节的变换等。

上述是 GAN 的一些应用例子,接下来会简单介绍 GAN 的原理以及其优缺点,当然也还有为啥等它提出两年后才开始有越来越多的 GAN 相关的论文发表。

1. 基本原理

GAN 的思想其实非常简单,就是生成器网络和判别器网络的彼此博弈。

GAN 主要就是两个网络组成,生成器网络(Generator)和判别器网络(Discriminator),通过这两个网络的互相博弈,让生成器网络最终能够学习到输入数据的分布,这也就是 GAN 想达到的目的–学习输入数据的分布。其基本结构如下图所示,从下图可以更好理解G 和 D 的功能,分别为:

  • D 是判别器,负责对输入的真实数据和由 G 生成的假数据进行判断,其输出是 0 和 1,即它本质上是一个二值分类器,目标就是对输入为真实数据输出是 1,对假数据的输入,输出是 0;
  • G 是生成器,它接收的是一个随机噪声,并生成图像。

在训练的过程中,G 的目标是尽可能生成足够真实的数据去迷惑 D,而 D 就是要将 G 生成的图片都辨别出来,这样两者就是互相博弈,最终是要达到一个平衡,也就是纳什均衡。

2. 优点

(以下优点和缺点主要来自 Ian Goodfellow 在 Quora 上的回答,以及知乎上的回答)

  • GAN 模型只用到了反向传播,而不需要马尔科夫链
  • 训练时不需要对隐变量做推断
  • 理论上,只要是可微分函数都可以用于构建 D 和 G ,因为能够与深度神经网络结合做深度生成式模型
  • G 的参数更新不是直接来自数据样本,而是使用来自 D 的反向传播
  • 相比其他生成模型(VAE、玻尔兹曼机),可以生成更好的生成样本
  • GAN 是一种半监督学习模型,对训练集不需要太多有标签的数据;
  • 没有必要遵循任何种类的因子分解去设计模型,所有的生成器和鉴别器都可以正常工作

3. 缺点

  • 可解释性差,生成模型的分布 Pg(G)没有显式的表达
  • 比较难训练, D 与 G 之间需要很好的同步,例如 D 更新 k 次而 G 更新一次
  • 训练 GAN 需要达到纳什均衡,有时候可以用梯度下降法做到,有时候做不到.我们还没有找到很好的达到纳什均衡的方法,所以训练 GAN 相比 VAE 或者 PixelRNN 是不稳定的,但我认为在实践中它还是比训练玻尔兹曼机稳定的多.
  • 它很难去学习生成离散的数据,就像文本
  • 相比玻尔兹曼机,GANs 很难根据一个像素值去猜测另外一个像素值,GANs 天生就是做一件事的,那就是一次产生所有像素,你可以用 BiGAN 来修正这个特性,它能让你像使用玻尔兹曼机一样去使用 Gibbs 采样来猜测缺失值
  • 训练不稳定,G 和 D 很难收敛
  • 训练还会遭遇梯度消失、模式崩溃的问题
  • 缺乏比较有效的直接可观的评估模型生成效果的方法

3.1 为什么训练会出现梯度消失和模式奔溃

GAN 的本质就是 G 和 D 互相博弈并最终达到一个纳什平衡点,但这只是一个理想的情况,正常情况是容易出现一方强大另一方弱小,并且一旦这个关系形成,而没有及时找到方法平衡,那么就会出现问题了。而梯度消失和模式奔溃其实就是这种情况下的两个结果,分别对应 D 和 G 是强大的一方的结果。

首先对于梯度消失的情况是D 越好,G 的梯度消失越严重,因为 G 的梯度更新来自 D,而在训练初始阶段,G 的输入是随机生成的噪声,肯定不会生成很好的图片,D 会很容易就判断出来真假样本,也就是 D 的训练几乎没有损失,也就没有有效的梯度信息回传给 G 让 G 去优化自己。这样的现象叫做 gradient vanishing,梯度消失问题。

其次,对于模式奔溃(mode collapse)问题,主要就是 G 比较强,导致 D 不能很好区分出真实图片和 G 生成的假图片,而如果此时 G 其实还不能完全生成足够真实的图片的时候,但 D 却分辨不出来,并且给出了正确的评价,那么 G 就会认为这张图片是正确的,接下来就继续这么输出这张或者这些图片,然后 D 还是给出正确的评价,于是两者就是这么相互欺骗,这样 G 其实就只会输出固定的一些图片,导致的结果除了生成图片不够真实,还有就是多样性不足的问题。

更详细的解释可以参考 令人拍案叫绝的Wasserstein GAN(https://zhuanlan.zhihu.com/p/25071913),这篇文章更详细解释了原始 GAN 的问题,主要就是出现在 loss 函数上。

3.2 为什么GAN不适合处理文本数据

  1. 文本数据相比较图片数据来说是离散的,因为对于文本来说,通常需要将一个词映射为一个高维的向量,最终预测的输出是一个one-hot向量,假设 softmax 的输出是(0.2, 0.3, 0.1,0.2,0.15,0.05),那么变为 onehot是(0,1,0,0,0,0),如果softmax输出是(0.2, 0.25, 0.2, 0.1,0.15,0.1 ),one-hot 仍然是(0, 1, 0, 0, 0, 0),所以对于生成器来说,G 输出了不同的结果, 但是 D 给出了同样的判别结果,并不能将梯度更新信息很好的传递到 G 中去,所以 D 最终输出的判别没有意义。
  2. GAN 的损失函数是 JS 散度,JS 散度不适合衡量不想交分布之间的距离。(WGAN 虽然使用 wassertein 距离代替了 JS 散度,但是在生成文本上能力还是有限,GAN 在生成文本上的应用有 seq-GAN,和强化学习结合的产物)

3.3 为什么GAN中的优化器不常用SGD

  1. SGD 容易震荡,容易使 GAN 的训练更加不稳定,
  2. GAN 的目的是在高维非凸的参数空间中找到纳什均衡点,GAN 的纳什均衡点是一个鞍点,但是 SGD 只会找到局部极小值,因为 SGD 解决的是一个寻找最小值的问题,但 GAN 是一个博弈问题。

对于鞍点,来自百度百科的解释是:

鞍点(Saddle point)在微分方程中,沿着某一方向是稳定的,另一条方向是不稳定的奇点,叫做鞍点。在泛函中,既不是极大值点也不是极小值点的临界点,叫做鞍点。在矩阵中,一个数在所在行中是最大值,在所在列中是最小值,则被称为鞍点。在物理上要广泛一些,指在一个方向是极大值,另一个方向是极小值的点。

鞍点和局部极小值点、局部极大值点的区别如下图所示:

4. 训练的技巧

训练的技巧主要来自 Tips and tricks to make GANs work–https://github.com/soumith/ganhacks。

1. 对输入进行规范化
  • 将输入规范化到 -1 和 1 之间
  • G 的输出层采用Tanh激活函数
2. 采用修正的损失函数

在原始 GAN 论文中,损失函数 G 是要 , 但实际使用的时候是采用 ,作者给出的原因是前者会导致梯度消失问题。

但实际上,即便是作者提出的这种实际应用的损失函数也是存在问题,即模式奔溃的问题,在接下来提出的 GAN 相关的论文中,就有不少论文是针对这个问题进行改进的,如 WGAN 模型就提出一种新的损失函数。

3. 从球体上采样噪声
  • 不要采用均匀分布来采样
  • 从高斯分布中采样得到随机噪声
  • 当进行插值操作的时候,从大圆进行该操作,而不要直接从点 A 到 点 B 直线操作,如下图所示
  • 更多细节可以参考 Tom White’s 的论文 Sampling Generative Networks(https://arxiv.org/abs/1609.04468) 以及代码 https://github.com/dribnet/plat
4. BatchNorm
  • 采用 mini-batch BatchNorm,要保证每个 mini-batch 都是同样的真实图片或者是生成图片
  • 不采用 BatchNorm 的时候,可以采用 instance normalization(对每个样本的规范化操作)
  • 可以使用虚拟批量归一化(virtural batch normalization):开始训练之前预定义一个 batch R,对每一个新的 batch X,都使用 R+X 的级联来计算归一化参数
5. 避免稀疏的梯度:Relus、MaxPool
  • 稀疏梯度会影响 GAN 的稳定性
  • 在 G 和 D 中采用 LeakyReLU 代替 Relu 激活函数
  • 对于下采样操作,可以采用平均池化(Average Pooling) 和 Conv2d+stride 的替代方案
  • 对于上采样操作,可以使用 PixelShuffle(https://arxiv.org/abs/1609.05158), ConvTranspose2d + stride
6. 标签的使用
  • 标签平滑。也就是如果有两个目标标签,假设真实图片标签是 1,生成图片标签是 0,那么对每个输入例子,如果是真实图片,采用 0.7 到 1.2 之间的一个随机数字来作为标签,而不是 1;一般是采用单边标签平滑
  • 在训练 D 的时候,偶尔翻转标签
  • 有标签数据就尽量使用标签
7. 使用 Adam 优化器
8. 尽早追踪失败的原因
  • D 的 loss 变成 0,那么这就是训练失败了
  • 检查规范的梯度:如果超过 100,那出问题了
  • 如果训练正常,那么 D loss 有低方差并且随着时间降低
  • 如果 g loss 稳定下降,那么它是用糟糕的生成样本欺骗了 D
9. 不要通过统计学来平衡 loss
10. 给输入添加噪声
  • 给 D 的输入添加人为的噪声
  • http://www.inference.vc/instance-noise-a-trick-for-stabilising-gan-training/
  • https://openreview.net/forum?id=Hk4_qw5xe
  • 给 G 的每层都添加高斯噪声
11. 对于 Conditional GANs 的离散变量
  • 使用一个 Embedding 层
  • 对输入图片添加一个额外的通道
  • 保持 embedding 低维并通过上采样操作来匹配图像的通道大小
12 在 G 的训练和测试阶段使用 Dropouts
  • 以 dropout 的形式提供噪声(50%的概率)
  • 训练和测试阶段,在 G 的几层使用
  • https://arxiv.org/pdf/1611.07004v1.pdf

参考文章:

  • Goodfellow et al., “Generative Adversarial Networks”. ICLR 2014.–https://arxiv.org/abs/1406.2661
  • GAN系列学习(1)——前生今世
  • 干货 | 深入浅出 GAN·原理篇文字版(完整)
  • 令人拍案叫绝的Wasserstein GAN–https://zhuanlan.zhihu.com/p/25071913
  • 生成对抗网络(GAN)相比传统训练方法有什么优势?–https://www.zhihu.com/question/56171002/answer/148593584
  • https://github.com/hindupuravinash/the-gan-zoo
  • What-is-the-advantage-of-generative-adversarial-networks-compared-with-other-generative-models–https://www.quora.com/What-is-the-advantage-of-generative-adversarial-networks-compared-with-other-generative-models
  • What-are-the-pros-and-cons-of-using-generative-adversarial-networks-a-type-of-neural-network-Could-they-be-applied-to-things-like-audio-waveform-via-RNN-Why-or-why-not–https://www.quora.com/What-are-the-pros-and-cons-of-using-generative-adversarial-networks-a-type-of-neural-network-Could-they-be-applied-to-things-like-audio-waveform-via-RNN-Why-or-why-not
  • https://github.com/soumith/ganhacks

注:配图来自网络和参考文章

 

现代 JS 流程控制:从回调函数到 Promises 再到 Async/Await

原文链接:Flow Control in Modern JS: Callbacks to Promises to Async/Await

译者:OFED

JavaScript 通常被认为是异步的。这意味着什么?对开发有什么影响呢?近年来,它又发生了怎样的变化?

看看以下代码:

result1 = doSomething1();
result2 = doSomething2(result1);

大多数编程语言同步执行每行代码。第一行执行完毕返回一个结果。无论第一行代码执行多久,只有执行完成第二行代码才会执行。

单线程处理程序

JavaScript 是单线程的。当浏览器选项卡执行脚本时,其他所有操作都会停止。这是必然的,因为对页面 DOM 的更改不能并发执行;一个线程
重定向 URL 的同时,另一个线程正要添加子节点,这么做是危险的。

用户不容易察觉,因为处理程序会以组块的形式快速执行。例如,JavaScript 检测到按钮点击,运行计算,并更新 DOM。一旦完成,浏览器就可以自由处理队列中的下一个项目。

(附注: 其它语言比如 PHP 也是单线程,但是通过多线程的服务器比如 Apache 管理。同一 PHP 页面同时发起的两个请求,可以启动两个线程运行,它们是彼此隔离的 PHP 实例。)

通过回调实现异步

单线程产生了一个问题。当 JavaScript 执行一个“缓慢”的处理程序,比如浏览器中的 Ajax 请求或者服务器上的数据库操作时,会发生什么?这些操作可能需要几秒钟 – 甚至几分钟。浏览器在等待响应时会被锁定。在服务器上,Node.js 应用将无法处理其它的用户请求。

解决方案是异步处理。当结果就绪时,一个进程被告知调用另一个函数,而不是等待完成。这称之为回调,它作为参数传递给任何异步函数。例如:

doSomethingAsync(callback1);
console.log('finished');

// 当 doSomethingAsync 完成时调用
function callback1(error) {
  if (!error) console.log('doSomethingAsync complete');
}

doSomethingAsync() 接收回调函数作为参数(只传递该函数的引用,因此开销很小)。doSomethingAsync() 执行多长时间并不重要;我们所知道的是,callback1() 将在未来某个时刻执行。控制台将显示:

finished
doSomethingAsync complete

回调地狱

通常,回调只由一个异步函数调用。因此,可以使用简洁、匿名的内联函数:

doSomethingAsync(error => {
  if (!error) console.log('doSomethingAsync complete');
});

一系列的两个或更多异步调用可以通过嵌套回调函数来连续完成。例如:

async1((err, res) => {
  if (!err) async2(res, (err, res) => {
    if (!err) async3(res, (err, res) => {
      console.log('async1, async2, async3 complete.');
    });
  });
});

不幸的是,这引入了回调地狱 —— 一个臭名昭著的概念,甚至有专门的网页介绍!代码很难读,并且在添加错误处理逻辑时变得更糟。

回调地狱在客户端编码中相对少见。如果你调用 Ajax 请求、更新 DOM 并等待动画完成,可能需要嵌套两到三层,但是通常还算可管理。

操作系统或服务器进程的情况就不同了。一个 Node.js API 可以接收文件上传,更新多个数据库表,写入日志,并在发送响应之前进行下一步的 API 调用。

Promises

ES2015(ES6) 引入了 Promises。回调函数依然有用,但是 Promises 提供了更清晰的链式异步命令语法,因此可以串联运行(下个章节会讲)。

打算基于 Promise 封装,异步回调函数必须返回一个 Promise 对象。Promise 对象会执行以下两个函数(作为参数传递的)其中之一:

  • resolve:执行成功回调
  • reject:执行失败回调

以下例子,database API 提供了一个 connect() 方法,接收一个回调函数。外部的 asyncDBconnect() 函数立即返回了一个新的 Promise,一旦连接创建成功或失败,resolve() 或 reject() 便会执行:

const db = require('database');

// 连接数据库
function asyncDBconnect(param) {

  return new Promise((resolve, reject) => {

    db.connect(param, (err, connection) => {
      if (err) reject(err);
      else resolve(connection);
    });

  });

}

Node.js 8.0 以上提供了 util.promisify() 功能,可以把基于回调的函数转换成基于 Promise 的。有两个使用条件:

  1. 传入一个唯一的异步函数
  2. 传入的函数希望是错误优先的(比如:(err, value) => …),error 参数在前,value 随后

举例:

// Node.js: 把 fs.readFile promise 化
const
  util = require('util'),
  fs = require('fs'),
  readFileAsync = util.promisify(fs.readFile);

readFileAsync('file.txt');

各种库都会提供自己的 promisify 方法,寥寥几行也可以自己撸一个:

// promisify 只接收一个函数参数
// 传入的函数接收 (err, data) 参数
function promisify(fn) {
  return function() {
      return new Promise(
        (resolve, reject) => fn(
          ...Array.from(arguments),
        (err, data) => err ? reject(err) : resolve(data)
      )
    );
  }
}

// 举例
function wait(time, callback) {
  setTimeout(() => { callback(null, 'done'); }, time);
}

const asyncWait = promisify(wait);

ayscWait(1000);

异步链式调用

任何返回 Promise 的函数都可以通过 .then() 链式调用。前一个 resolve 的结果会传递给后一个:

asyncDBconnect('http://localhost:1234')
  .then(asyncGetSession)      // 传递 asyncDBconnect 的结果
  .then(asyncGetUser)         // 传递 asyncGetSession 的结果
  .then(asyncLogAccess)       // 传递 asyncGetUser 的结果
  .then(result => {           // 同步函数
    console.log('complete');  //   (传递 asyncLogAccess 的结果)
    return result;            //   (结果传给下一个 .then())
  })
  .catch(err => {             // 任何一个 reject 触发
    console.log('error', err);
  });

同步函数也可以执行 .then(),返回的值传递给下一个 .then()(如果有)。

当任何一个前面的 reject 触发时,.catch() 函数会被调用。触发 reject 的函数后面的 .then() 也不再执行。贯穿整个链条可以存在多个 .catch() 方法,从而捕获不同的错误。

ES2018 引入了 .finally() 方法,它不管返回结果如何,都会执行最终逻辑 – 例如,清理操作,关闭数据库连接等等。当前仅有 Chrome 和 Firefox 支持,但是 TC39 技术委员会已经发布了 .finally() 补丁

function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // 清理操作放这儿!
  });
}

使用 Promise.all() 处理多个异步操作

Promise .then() 方法用于相继执行的异步函数。如果不关心顺序 – 比如,初始化不相关的组件 – 所有异步函数同时启动,直到最慢的函数执行 resolve,整个流程结束。

Promise.all() 适用于这种场景,它接收一个函数数组并且返回另一个 Promise。举例:

Promise.all([ async1, async2, async3 ])
  .then(values => {           // 返回值的数组
    console.log(values);      // (与函数数组顺序一致)
    return values;
  })
  .catch(err => {             // 任一 reject 被触发
    console.log('error', err);
  });

任意一个异步函数 rejectPromise.all() 会立即结束。

使用 Promise.race() 处理多个异步操作

Promise.race() 与 Promise.all() 极其相似,不同之处在于,当首个 Promise resolve 或者 reject 时,它将会 resolve 或者 reject。仅有最快的异步函数会被执行:

Promise.race([ async1, async2, async3 ])
  .then(value => {            // 单一值
    console.log(value);
    return value;
  })
  .catch(err => {             // 任一 reject 被触发
    console.log('error', err);
  });

前途光明吗?

Promise 减少了回调地狱,但是引入了其他的问题。

教程常常不提,整个 Promise 链条是异步的,一系列的 Promise 函数都得返回自己的 Promise 或者在最终的 .then().catch() 或者 .finally() 方法里面执行回调。

我也承认:Promise 困扰了我很久。语法看起来比回调要复杂,好多地方会出错,调试也成问题。可是,学习基础还是很重要滴。

延伸阅读:

Async/Await

Promise 看起来有点复杂,所以 ES2017 引进了 async 和 await。虽然只是语法糖,却使 Promise 更加方便,并且可以避免 .then() 链式调用的问题。看下面使用 Promise 的例子:

function connect() {

  return new Promise((resolve, reject) => {

    asyncDBconnect('http://localhost:1234')
      .then(asyncGetSession)
      .then(asyncGetUser)
      .then(asyncLogAccess)
      .then(result => resolve(result))
      .catch(err => reject(err))

  });
}

// 运行 connect 方法 (自执行方法)
(() => {
  connect();
    .then(result => console.log(result))
    .catch(err => console.log(err))
})();

使用 async / await 重写上面的代码:

  1. 外部方法用 async 声明
  2. 基于 Promise 的异步方法用 await 声明,可以确保下一个命令执行前,它已执行完成
async function connect() {

  try {
    const
      connection = await asyncDBconnect('http://localhost:1234'),
      session = await asyncGetSession(connection),
      user = await asyncGetUser(session),
      log = await asyncLogAccess(user);

    return log;
  }
  catch (e) {
    console.log('error', err);
    return null;
  }

}

// 运行 connect 方法 (自执行异步函数)
(async () => { await connect(); })();

await 使每个异步调用看起来像是同步的,同时不耽误 JavaScript 的单线程处理。此外,async 函数总是返回一个 Promise 对象,因此它可以被其他 async 函数调用。

async / await 可能不会让代码变少,但是有很多优点:

  1. 语法更清晰。括号越来越少,出错的可能性也越来越小。
  2. 调试更容易。可以在任何 await 声明处设置断点。
  3. 错误处理尚佳。try / catch 可以与同步代码使用相同的处理方式。
  4. 支持良好。所有浏览器(除了 IE 和 Opera Mini )和 Node7.6+ 均已实现。

如是说,没有完美的…

Promises, Promises

async / await 仍然依赖 Promise 对象,最终依赖回调。你需要理解 Promise 的工作原理,它也并不等同于 Promise.all() 和 Promise.race()。比较容易忽视的是 Promise.all(),这个命令比使用一系列无关的 await 命令更高效。

同步循环中的异步等待

某些情况下,你想要在同步循环中调用异步函数。例如:

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}

不起作用,下面的代码也一样:

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}

循环本身保持同步,并且总是在内部异步操作之前完成。

ES2018 引入异步迭代器,除了 next() 方法返回一个 Promise 对象之外,与常规迭代器类似。因此,await关键字可以与 for ... of 循环一起使用,以串行方式运行异步操作。例如:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}

然而,在异步迭代器实现之前,最好的方案是将数组每项 map 到 async 函数,并用 Promise.all() 执行它们。例如:

const
  todo = ['a', 'b', 'c'],
  alltodo = todo.map(async (v, i) => {
    console.log('iteration', i);
    await processSomething(v);
});

await Promise.all(alltodo);

这样有利于执行并行任务,但是无法将一次迭代结果传递给另一次迭代,并且映射大数组可能会消耗计算性能。

丑陋的 try/catch

如果执行失败的 await 没有包裹 try / catchasync 函数将静默退出。如果有一长串异步 await 命令,需要多个 try / catch 包裹。

替代方案是使用高阶函数来捕捉错误,不再需要 try / catch 了(感谢@wesbos的建议):

async function connect() {

  const
    connection = await asyncDBconnect('http://localhost:1234'),
    session = await asyncGetSession(connection),
    user = await asyncGetUser(session),
    log = await asyncLogAccess(user);

  return true;
}

// 使用高阶函数捕获错误
function catchErrors(fn) {
  return function (...args) {
    return fn(...args).catch(err => {
      console.log('ERROR', err);
    });
  }
}

(async () => {
  await catchErrors(connect)();
})();

当应用必须返回区别于其它的错误时,这种作法就不太实用了。

尽管有一些缺陷,async/await 还是 JavaScript 非常有用的补充。更多资源:

JavaScript 之旅

异步编程是 JavaScript 无法避免的挑战。回调在大多数应用中是必不可少的,但是容易陷入深度嵌套的函数中。

Promise 抽象了回调,但是有许多句法陷阱。转换已有函数可能是一件苦差事,·then() 链式调用看起来很凌乱。

很幸运,async/await 表达清晰。代码看起来是同步的,但是又不独占单个处理线程。它将改变你书写 JavaScript 的方式,甚至让你更赏识 Promise – 如果没接触过的话。

微信小程序开发资源汇总

QQ交流群

目录

推荐

置顶

官方文档

工具

  • WePY ★12k+ – 支持组件化的小程序开发框架
  • mpvue ★11k+ – 基于 Vue.js 的小程序开发框架,从底层支持 Vue.js 语法和构建工具体系
  • Taro ★3.9k – 使用 React 的方式开发小程序的框架,同时支持生成多端应用
  • Labrador ★1.5k – 支持 ES6/7 的微信小程序组件化开发框架
  • wept ★1.8k – 微信小程序实时运行环境
  • wafer ★1.6k – 快速构建具备弹性能力的微信小程序
  • wechat_web_devtools ★600+ – Linux 下微信开发者工具
  • minapp ★400+ – TypeScript 版小程序开发框架(兼容原生小程序代码)
  • tina ★300+ – 轻巧的渐进式微信小程序框架
  • xpmjs ★100+ – 微信小程序云端增强 SDK
  • WeApp-Workflow ★100+ – 基于 Gulp 的微信小程序前端开发工作流
  • gulp-wxa-copy-npm – 微信小程序 gulp 插件,解决 npm 包管理和 babel-runtime
  • weact – 用 JSX 快速开发小程序
  • socket.io-mp-client – 微信小程序 socket.io 客户端
  • wxa – 方便的小程序开发框架
  • px2rpx – Px 转 Rpx 在线工具
  • wxml-parser – JavaScript WXML parser
  • weappx – 基于 redux 的数据层管理框架
  • weapp-start – 基于插件机制的开发脚手架,改善原生小程序开发体验
  • Egret Wing – 支持微信小程序实时预览的 IDE
  • wxapp-graphql – 小程序GraphQL客户端

插件

教程

视频教程

文章

组件

Demo

from:https://github.com/justjavac/awesome-wechat-weapp

中国互联网20年简史(1998-2018),告诉你本质是什么、规律是什么

我先说说互联网三大支柱:

1、内容:新闻&文学、音乐&视频

2、社区+内容

3、游戏

4、电子商务

我们来简单回顾一下中国互联网20年。咱们先从1998年开始。

一、1998年:内容门户元年

1998年2月,张朝阳正式成立搜狐。给中国产业带来了VC风险投资、CXO职业经理人团队,让中国老百姓认知了中国互联网。

1998年,四通利方和北美华人网华渊网合并,成立新浪网。给中国产业带来了并购、创始人被踢出团队。

1997年,网易成立。1998年推出网易免费电子邮件服务。

显然,这是内容门户之年。

虽然1998年腾讯也成立了,但显然大家都没注意到它。但最后四大门户是:搜狐、新浪、网易、腾讯。

还有一家公司成立,那就是在线网络游戏公司:联众,出自希望公司UCDOS软件团队。

你看,内容门户+BBS论坛社区+IM+游戏,在20年前,就同时出现了。中国互联网主航道,都基本出现。

二、1999年:电子商务元年

内容门户大热到极点。连联想都做了一个FM365。更别说还有中华网、TOM网、多来米这些富家子弟玩的。这些显然都是后知后觉了。

1999年11月,当当B2C成立,标品卖书,客单价也低,正好做启蒙。1999年,阿里巴巴B2B也成立了,搞黄页,帮助中国小商品制造出口走向国际。

1999年还成立了一家当时没人知道的公司:携程。

1999年还成立了一家公司:51job,网上找工作。其实招聘的本质是:黄页信息,只不过这个黄页不是企业信息,而是个人简历信息。

这是中国电子商务的开始。他们真是先知先觉。

三、2000年:内容门户上市年

网易、搜狐、新浪都在纳斯达克上市。这才短短3年时间。

2000年1月,百度成立。

四、2001年:移动手机元年

美国互联网投资泡沫破裂,也影响了中国。很多后来进入者都纷纷转型去开发企业软件,因为他们熟悉互联网、熟悉当时火热的Web开发技术和JAVA开发技术,所以他们一开始就做供应链互联网平台。当时中国企业软件商还在纷纷用VB/PB/DELPHI开发局域网企业内部管理软件呢。

但是在前几年互联网资本的推动下,基础设施:通信&数据传输网络也在迅猛发展。诺基亚在这一年市值逼近2500亿美金,中国出现国产手机热,以波导为代表,IT厂商联想也介入进来,家电厂商海尔也介入,电话座机厂商步步高(Vivo和Oppo的母公司)也介入。

五、2002年:移动手机内容SP元年

2002年,中国移动和中国联通的手机终于能短信互通了。好不容易2000年上市的三大门户都熬的受不了,想寻找到立刻变现的业务。

彩铃、短信报热。网易首先抓住这个机遇(嘿嘿,大家可以另外寻找网易和张静君和广州电信局的渊源)。中国互联网企业做SP业务开始了,尤其是把网上的新闻和段子通过短信传播。

六、2003年:网络游戏元年

2003年7月,盛大公司发布传奇世界游戏,引发了中国网络游戏热。随后,九城、完美时空、巨人等等公司跟进,造就了中国很多赚的盆满钵满的网络游戏公司。这个行当也给中国积累了大量的产品经理、制作人、美工人才、音效人才、3D技术、网络技术。

七、2004年:中国互联网第一波集中上市收割年

2004年,移动手机内容SP厂商:空中网、掌上灵通网,上市。

2004年,垂直金融内容门户公司:金融界,上市

2004年,社区+IM+游戏厂商腾讯上市。

2003年12月(相当于2004年了吧),中国电子商务第一个公司:携程,上市。携程。从1999年中国电子商务开始元年,到2003年,这才短短4年时间。

2004年,泛电商人才招聘网:51job上市。

2004年,网上卖机票电商网:E龙上市。

2004年,网络游戏厂商:盛大,上市。

从1998年算来,到2004年,6年时间,中国互联网开始了第一波收割热潮。

八、2005年:Web2.0社区内容元年

2005年,超女最火的一届出现:李宇春、张靓颖、何洁。大众用短信投票引爆全国。这是民众的、互动的狂欢。

2005年,新浪博客发布。

2005年3月,豆瓣成立。这是一个点评的网站。

2005年12月,58同城成立,这是一个个人黄页发布的网站。

额外,搜索引擎公司:百度,在这一年上市了。

七、2006年:网络视频内容元年

2006年,六间房视频网成立。2006年,李善友的酷六网成立。2006年,优酷网成立。2006年,Google并购Youtube网。

另外,2005年4月,土豆网成立。2005年,迅雷公司正式成立。2005年,VeryCD(电驴下载)公司正式成立。这都是和视频电影的崛起有关联的。

过去大家只能看文字图片内容网站,做图片钻展广告。现在有了更直观的视频网站,而且每部电影都可以贴片做广告了。

但是并不代表文字内容网站失落,相反,中国网络文学崛起,你看:2006年,天下霸唱开始写鬼吹灯引爆盗墓类小说、2006年当年明月开始写明朝那些事儿引爆写史类小说,2006年月关开始写回到明朝当王爷引爆穿越类小说。

2006年,其实还有一个大混战,就是楼宇广告大战。成立于2003年的分众传媒,2005年上市,2006年并购了聚众传媒、框架传媒。

八、2007年:电子商务高速成长年

2007年,阿里巴巴B2B业务在香港上市。

但是大家也别忘了,2007年8月,中关村一个经营了快10年的拥有11家线下店铺IT硬件批发零售商,融了一笔钱,开始关闭自己的线下店,专注高速发展线上电商自营零售,那就是京东。

2007年,Yes!PPG成立,主打自创品牌的男士商务休闲服装。2007年,模仿Yes!PPG的凡客诚品也成立了。这是中国自创品牌的电子商务公司,集:研发设计、供应链采购、委托加工、质量控制、商品电商零售、仓储物流配送于一体。

2007年,也是中国快捷酒店大战的一年。如家、汉庭、锦江之星、7天、莫泰、格林豪泰、布丁、桔子酒店…,好多好多。

悄悄另外说一句:2007年,iPhone上市了,大内存、高速CPU、高速WIFI网络、大屏幕、多点触摸输入操作,这几乎是和过去的智能手机有天壤差别:小内存、小屏幕、GPRS联网、笔触输入或键盘输入。中国移动互联网暗示着要大规模爆发了。

九、2008年:智能手机元年

2008年,是中国互联网的十周年。但是这一年也暗示着,中国PC Web互联网即将开始走向最高峰,最高峰也意味着下一个时代已经在悄然启动。

2008年,Google正式发行Android操作系统。

2008年,Apple发布iPhone3G手机,最最关键的是,智能手机的高利润盈利模式、流量口模式出现了:App Store。想想百度收购91,其实也想占领智能移动互联网的入口,没想到独立的应用商店根本不存在活路,全被智能手机厂商在底层釜底抽薪了,人家有小米应用商店、华为应用商店…,全是硬件+软件一体化的。

2008年,山寨手机热啊。在联发科一体化MTK模组的支持下,在华强北电子零件集散批发、深圳电子代工场云集的支撑下,中国人制造手机的门槛被放到最低。

十、2009年:交战年

2009年,新浪微博发布显然是最显眼的。随后,各大内容门户网站纷纷发布类微博产品服务。微博,120个字,类似短信使用体验却可以带图片(比短信好),可以关注名人还可以评论互动(不像QQ得双向关注),也可以随手转发。

除了微博这种类互联网手机上的多媒体短信产品大战,这一年整个互联网界都不平静。

淘宝发布购物搜索,屏蔽百度爬虫。当然,微软也发布了Bing搜索引擎来PKGoogle搜索引擎。

腾讯和搜狗针对输入法进行诉讼。因为输入法在用户需求探知、弹窗引流、输入汉字导流方面具有很高的入口杀伤力。

网络游戏《魔兽》的代理运营权,也在九城、网易之间拉锯。

还有2008年很火的基于QQ社交网络关系的页游:偷菜,开心网和千橡开心网也进行了诉讼。

为什么2009年如此不平静?就是因为PC Web互联网进入成熟期,产业要开始高度集中,要进行整合,不可避免要咔咔作响。

2009年,中国三大运营商均获得3G牌照,移动互联网眼看着眼看着要来临了。

十一、2010:电商爆发年

2010年大家最记忆犹新的肯定是百团大战,大量团购网站出现,尤其以2010年3月美团成立为标志。中国电子商务:衣食住行,衣,有淘宝;食开始了,就是这些团购网;住,有曾经的如家快捷酒店大战,也有58的租房二手房黄页广告聚焦房产行业大战;行,有过去的携程、E龙、同程、去哪儿大战。除了衣是实物零售业外,其他食住行都是服务电商业,即:在网上吸引消费者流量,在网上获得优惠折扣,在网上下订单,甚至在网上进行支付,然后在线下进行到场消费。

2010年,在衣这个事情上,还有一个轰动的事,那就是淘宝双十一热。但这已经其实是第二届了(第一届是2009年双11),但就如超女一样,也是第一届举办完了大部分人都不知道,第二届才引爆。双11对于淘宝的意义非常大。而京东,在2010年也迎来了爆发式增长:开放POP平台、一线城市极速达、收购网银在线介入金融业务。而且京东主动发起了图书大战,直指当当老大哥(当当可谓是中国互联网最早的电商零售)。还有一家发源于手册邮寄购买最终爆发于电商的服装零售公司:麦考林,也在2010年上市了,成为了第一家上市的电子商务零售公司(2007年阿里巴巴B2B业务上市。)

1999年,中国电商元年;2007年,中国电商成长年;2010年,中国电商爆发年。

当然,在PCWeb纯互联网领域,2010年也发生了一件非常大的事情,就好像两个黑洞碰撞一样巅峰对决,那就是3Q大战。2010年11月21日,在工信部、网信办的协调下,两个公司达成和解,不互相封杀卸载对方的软件。中国PC 互联网终于走到了尽头。就好像红巨星要爆炸要产生超新星是一样一样的。从1998年开始的PC Web互联网,走到了2008年智能手机元年,2009年互联网产业整合交战,最终在2010年3Q大战最高巅峰对决中收场。

再想想中国移动互联网,2008年是智能手机元年,今年是2018年,也十年了,今年的大战是腾讯集团和今日头条的大战。相信到2020年,也会如超新星一样,那时候是什么互联网呢?已经有了ADSL宽带PC Web 互联网和4G 移动 App互联网,难道那时候在5G的引导下,会出现万物互联网?

4G移动App互联网,以移动智能手机为主产业,那5G万物互联网,什么是主产产业呢?难道是智能汽车?

十二、2011:中国移动互联网元年

这一年,有两个重要级产品出现,一个是智能手机:小米和MIUI OS出现,一个是原生移动IM:微信出现。

智能手机硬件、Andriod操作系统、应用商店、IM通信,这种组合层次颇值得人寻味。在万物互联网时代会不会出现的最初超级应用也是这样?

其实早在2G GPRS时代,在2008年,手机QQ已经很多人使用了,但是微信出现了,手机QQ没落了。微信的出现正好和iPhone成为智能手机事实标准是同步的。从iPhone开始,过去手机是五花八门各种样子各种功能,尤其在山寨手机之时到达最高潮,然后在iPhone带动下,全世界的手机都变成一模一样的大体模样了。也就是说,都是大屏幕、无键盘。微信的崛起第一步就是:语音消息、手机通讯录导入、摇一摇周边。你看,全是原生应用了手机的独特特性。大家在思考万物互联网的时候也应该这样思考,要充分应用硬件的独特特性,而不是把大象装在冰箱里(比如很多人把企业管理软件精简功能做成App)。

十三、2012年:中国移动互联网发展年

这一年,中国手机网民数量超过了电脑上网网民数量(其实2013年中国政府才要求运营商强力推4G)。

这一年,中国移动互联网高速发展,大家都纷纷把自己的PC Web互联网的功能翻新成App版本。

华为、锤子也正式宣布进入智能手机领域。360和海尔也联合推出超级战舰手机。就连腾讯也谨慎的做了一个手机ROM。大家纷纷想从智能手机硬件、智能路由器和DNS、OS、应用商店、通信IM超级APP,一层层地釜底抽薪。

十四、2013:中国移动互联网爆炸年

3年啊,同志们。太快了,怪不得劳模雷军说:现在就连睡觉都觉得是浪费。

这一年,小米开始构建自己的智能产品生态,发布了好几款智能产品。这一年,乐视智能电视发布,乐视开始引爆江湖。

在这一年有两个关键事件决定了这一年是移动互联网爆炸年。一个是微信发布了游戏流量入口、打飞机小游戏狂潮掀起了移动游戏App的热潮。移动互联网,怎么能够少了游戏这一个半边天呢。微信在这一年还发布了公众号。没有媒体人的卷入,就不可能有大影响力大声音。微信也是在这一年狂发展的。

另外一个大事件就是,也就是从这一年开始,BAT们都开始做好自己的主流超级流量App,大力发展Open API开放平台,大力做投资并购。中国移动互联网为什么发展的这么快,关键就是有三大要素:超级流量、Open API和平台、资本投资和并购进入山头帮派时代。同志们,这句话要标红啊。这是过去中国互联网界从未有过的事情啊。这是中国互联网的并购年,其中最大的收购案就是百度以18.5亿美金收购91无线。

十五、2014:中国企业服务元年

在这一年,中国电商出现了一件大事:京东、阿里纷纷上市。京东和阿里占据中国线上B2C零售80%市场份额,构成了真正的双子星结构。这种市场结构,意味着这个市场已经成为寡头市场。现在再进入这个市场,不可能再出现第三巨头了,从第三名到最后一名,所有加总才能占据最多20%市场。从目前来看,就连拼多多这种拼团模式、云集微店众包销售模式,也只能成诸侯而不能成王。

虽然说,云计算创业三小龙:Ucloud、七牛、青云,都是2010-2011间成立的。但是,中国企业服务投资规模第一波热潮却真正来自2014年。其实不说2006年Amazon发布AWS,就说阿里云,也是在2009年10月写下第一行代码,2012年做天猫聚石塔项目,2013年换帅胡晓明开始高速发展。而中国IaaS云计算的高潮,还要等到2016年,那一年,所有互联网大佬、所有中国系统集成商大佬都已经进入IaaS公有云和私有云的市场。不过很多人却还后知后觉,直到2015年中国企业服务投资最高潮,很多人才匆忙杀入了这个行业,才匆忙升级要做SaaS软件。

这一年还有件事,就是菜鸟网络在这年成立了。菜鸟网络不搞仓库不买卡车不跑运输不招业务人员,但是菜鸟网络凭借淘宝天猫的订单流量、凭借自己的大数据建模技术,来做到智能的仓储物流调度,抽取中间的调度佣金。这本质上是电商+企业服务的混合体。

这一年还有件事,是滴滴的快速崛起。滴滴后来被人们笑话段子都演变成了滴滴模式(拼多多拼团也是种模式、团购秒杀倒计时也是种模式),什么滴滴打人、滴滴代喝、滴滴代嫁等等,哈哈哈。但是,滴滴代驾、滴滴拼车业务真的出现了。滴滴的核心是:根据地理位置(智能手机独特特性)、根据业务大数据和用户画像,智能匹配最合适的打车人和出租车,抽取中间的调度佣金。这本质也是电商+企业服务的混合体。

也就是说,智能调度匹配抽佣金,这个模式,会在中国业务+IT服务的各行各业中,都会形成落地的业务场景。这就是一种经过验证的以技术为本的商业模式。这个模式,非常值得中国企业服务商去探索借鉴思考。

十六、2015:中国互联网金融元年

这一年,在中央的大众创业万众创新的推动下,互联网金融开始热潮。当然,中国电子商务要高速发展,没有金融的支撑也是不能更快更大的发展。

所以这一年,P2P热、众筹热、消费贷热、供应链金融热都起来了。

但更突出的是:微信支付和支付宝支付开始大战。大家纷纷通过各种扫街、发起补贴活动,到处贴二维码,可以扫码支付。现在,就连买菜买鸡蛋夹肉饼,都可以用手机扫码。这种便捷性是全世界最领先的。

十七、2016年:智能元年

这一年,直播热崛起。但是这种直播和过去最早YY的直播还不一样。现在的直播最关键应用了实时美颜技术、虚拟AR技术。

这一年,从支付宝AR找福字、美颜App自带兔子耳朵虚拟表情开始,就发现今年的不一样了。那就是现实和虚拟的实时结合。

所以这一年,也带动了AR、VR设备热。也带动了人工智能技术热,先从人脸识别开始。

直播热从娱乐很快扩展到了电商,从虚拟送礼盈利走向推荐商品带货盈利。这也造就了2016年的微商热。这不同于过去的微商拼命加5000人拼命加群拼命发朋友圈鸡汤发微商广告的模式。这一年的微商都在实时美颜直播技术中统统成为了网红。

直播打赏热(这里也有上一年移动支付大战红包大战的功劳),也渐渐带动了另外一个正经事:知识网红(如罗振宇)+知识付费(如得到)。

这一年,还出现了共享单车大战,最后连颜色都不够用了。这里面最有名的论战就是马化腾和朱啸虎的言战,焦点是谁能笑到最后。马化腾认为是一定要是智能设备,如智能车锁,这显然是技术派的看法。而朱啸虎代表运营派,也就是说:最成熟的产品和消费习惯,最广阔的市场,用资本轰开。现在,朱啸虎的OfO陷入尴尬境地,朱啸虎后来都暗示服了软,显然,智能才是核心。

十八、2017年:新零售元年

2017年说什么热,那必须是新零售。新零售的本质是用智能传感设备、人工智能技术,实现线下数据收集和线上电商是一样的效果。大家切记切记。

虽然说,有人把新零售看做是线下,用正规企业运营方法再重做一次,就如同当年如家他们用资本+企业标准运营管理方法重塑中国快捷酒店业一样。

技术派:无人零售商店、无人货架&货柜、智能购物车、智能称重台、人脸识别进门、人脸识别自动支付

运营派:生鲜、生鲜+餐饮堂食、连锁便利店、中央厨房供应链….

十九、2018年:

2018年,中国互联网金融热已经过去了3年。今年卷土重来的是:区块链。创建X币项目、发币上交易所市,成了主流割韭菜模式。看不清看不清。

在去年直播热、直播技术、直播打赏普及成熟的基础上,快手、抖音短视频热崛起。但是这种短视频又和过去的移动视频热不一样。因为除了技术流畅、打赏付费习惯成熟外,现在抖音又多了附近、朋友、互动。这简直是下一代社交网络的潜力啊。怪不得腾讯今天上半年从收购到打击抖音到内部赛马做了N多短视频App,根源就在于此。

我过去就说过,QQ发源于台式机拨号上网文字时代,微信发源于智能手机4G上网语音时代,那么下一代社交网络工具一定是基于万物互联5G上网视频时代。

从2008年智能手机元年开始算起,今年也已经十年了。智能手机移动互联网时代也要高潮地结束了。今年,小米也要在港交所上市了。

一个时代即将结束(还有2年最后的两个黑洞大碰撞,我猜是腾讯集团和今日头条集团),那么我们在今年,确实应该多静下来思考思考,几个好友坐下来重新回归历史一片空白该想想了、放开脑洞发散性讨论讨论了。

下一代主流硬件是什么?下一代主流OS是什么?

下一代主流社交网络工具是什么?下一代搜索是什么(是视觉识别和语音交互吗)?

下一代内容是什么样子的?比如说新闻、文学、视频、音乐…。

下一代游戏是什么样子的(是ARVR吗)?

下一代电子商务是什么样子的?下一代后端企业服务是什么样子的?

这里边,有你吗?

下一代商业模式的核心要素是什么?除了:流量、资本、技术和数据以及开放平台外,还有什么新的要素?

from:https://mp.weixin.qq.com/s/X195WPlHz6IyMyg-lpc9Yg