阅读视图

发现新文章,点击刷新页面。
🔲 ☆

【直观算法】二叉搜索树算法总结

【阅读时间】
【阅读内容】结合Leetcode相关算法题总结二叉搜索树的相关算法,包括基本的二叉搜索构建和应用,附带一些关于AVL树,红黑树的基本概念梳理

是什么

二叉搜索树(Binary Search Tree)BST是大名鼎鼎的搜索算法。在算法界,$O(n)$ 到 $O(\log_2 n)$ 的效率优化大多和BST有关

用白话文来说,二叉搜索树是一颗对于所有节点左孩子 < 根右子树 > 根的二叉树

基本操作

构建

相关例题:108. Convert Sorted Array to Binary Search Tree

已经给出了定义,Leetcode中有一道将升序数组转换成平衡二叉搜索树的题目。根据二叉树遍历一节的内容,中序遍历的顺序是左 ➜ 根 ➜ 右,再结合二叉搜索树的定义。观察知,二叉搜索树的中序遍历就是一个升序数组。那么问题就转换成了,哪颗平衡二叉树的中序遍历是这个升序数组

因为题目要求平衡二叉树,保证所有子树的高度一样,必须二分输入序列

假设输入序列为[-10,-3,0,5,9],根节点一定在mid = (start + end) // 2 位置,由递归思维:假设再次调用的函数的返回值是已经完成的子树,也就是说只需把[0, mid-1]代表的树作为左子树,和[mid+1, end]代表的树作为右子树即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
if not nums: return None

mid = len(nums) // 2
root = TreeNode(nums[mid])
root.left = self.sortedArrayToBST(nums[ : mid])
root.right = self.sortedArrayToBST(nums[mid+1 : ])

return root

查找

相关例题:700. Search in a Binary Search Tree

最常见的二叉树操作,查找一个对应节点,平均查找长度为 $\log_2(n)$ 。二叉搜索树性质,左孩子<根<右孩子,按照规律进行递归即可。省略迭代写法,只需要按照顺序进行一个节点一个节点顺下即可,非常简单

1
2
3
4
5
6
7
8
9
10
class Solution:
def searchBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
if not root or root.val == val:
return root
return self.searchBST(root.left, val) if val < root.val else self.searchBST(root.right, val)

判断

相关例题:98. Validate Binary Search Tree

【输入】给定一个树的结构【操作】判断这颗树是不是二叉搜索树【输出】True or False

① 使用中序遍历,结果是升序序列则为二叉搜索树(前面讲定义的时候已经讲解的原因)

② 去重复操作。①中在遍历过程就可做判断,不需要重新再做一次升序判断

这里实现使用迭代写法,递归写法比较简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""

if root is None: return True
stack, inorder = [], []
p_node = root
pre_node_val = float("-inf")
while stack or p_node:
while p_node:
stack.append(p_node)
p_node = p_node.left
cur_node = stack.pop()
inorder.append(cur_node.val)
if pre_node_val >= cur_node.val:
return False
pre_node_val = cur_node.val
if cur_node.right:
p_node = cur_node.right
return True

删除

二叉查找树中的删除节点操作,详见链接

需要分为3种情况进行讨论

  • 没有孩子的节点 ➜ 直接将它删除即可,它的父节点的孩子替换成空
  • 只有一个孩子的节点 ➜ 直接上升孩子的位子替代被删除的即可
  • 两个孩子的节点 ➜ 此种情况比较麻烦,需要参看详细链接

相关题目

🔲 ☆

【直观算法】二叉树遍历算法总结

【阅读时间】7 - 10 min | 4300字
【阅读内容】结合应用场景,总结有关二叉树遍历的所有算法和对应Leetcode题目编号。基于Python代码,给出完整逻辑链。希望给读者一个线头,让你永远忘不了这几个遍历算法

遍历算法总览

遍历的含义就是把树的所有节点(Node)按照某种顺序访问一遍。包括前序中序后续广度优先(队列),深度优先(栈)5中遍历方法

遍历方法 顺序 示意图 应用
前序 根 ➜ 左 ➜ 右
想在节点上直接执行操作(或输出结果)使用先序
中序 左 ➜ 根 ➜ 右
二分搜索树中,中序遍历的顺序符合从小到大(或从大到小)顺序的
要输出排序好的结果使用中序
后序 左 ➜ 右 ➜ 根
后续遍历的特点是在执行操作时,肯定已经遍历过该节点的左右子节点
适用于进行破坏性操作
比如删除所有节点,比如判断树中是否存在相同子树
广度优先 层序,横向访问
树的高度非常高(非常瘦)
使用广度优先剑节省空间
深度优先 纵向,探底到叶子节点
每个节点的子节点非常多(非常胖),使用深度优先遍历节省空间
(访问顺序和入栈顺序相关,想当于先序遍历)

关于应用部分,选择遍历方法的基本的原则:更快的访问到你想访问的节点。先序会先访问根节点,后序会先访问叶子节点

需要说明的是,递归是一种拆分思维的具体问题类别的思维方法,其核心的思维我觉得和动态规划非常类似,都是假设子节点搞定了我现在应该干什么这个问题

先确定Python语言下的TreeNode定义

1
2
3
4
5
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None

需要输出遍历结果时直接输出保存val的数组即可

关于递归算法的解释,博主打算写一份【直观算法】汉诺塔问题最全解答,过后可能会更新,是一篇小品文,比较短,这篇文章只是希望让所有阅读的人能一次就直观的搞明白汉诺塔的算法是怎么做的,永远记住它,也搞懂递归算法

三种遍历方法,都有一个特点,无论是先序根 ➜ 左 ➜ 右,中序左 ➜ 根 ➜ 右,后序左 ➜ 右 ➜ 根,所谓的访问顺序,根是最重要,根才代表了访问这个动作(在我们的代码中,就是把节点中的加入到输出数组中),⭐️而在的位置决定了是否可以访问的条件

比如对于中序来说,的后面,意味着,只要当前操作的节点有节点,就不能输出里面的值

对于后序来说,有了这个直观理解,对理解三者的迭代算法有帮助

先序遍历

在线刷题:Leetcode 44. Binary Tree Preorder Traversal

递归算法

所谓递归(Recursive),即把函数本身看成一个已经有解的子问题

定义函数preorderTraversal(self, node)返回以node为答案的先序遍历结果的数组,假设它的两个孩子node.leftnode.right已经搞定了,即可以返回答案的输出数组。那么思考最终的输出数组是什么样的,很明显要满足根 ➜ 左 ➜ 右的规则,应该返回[node.val] + preorderTraversal(self, node.left) + preorderTraversal(self, node.right)(函数返回的就是一个数组,只需要把它们拼接起来即可)

之后再完善防御性编程的基本步骤(保证函数输入有效),按照这个思路就可以写出先序遍历的递归代码。Python代码的特点是可读性比较强,这样一行代码简洁明了,能简洁的表达上面的逻辑链推理过程

1
2
3
4
5
6
7
8
class Solution:
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []
return [] if root is None else [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

当然,如果不使用Python,在语法上无法写的这么简短。常见的标准写法是使用helper()函数,具体实现见下

1
2
3
4
5
6
7
8
9
10
def preorderTraversal1(self, root):
result = []
self.helper(root, result)
return result

def helper(self, root, result):
if root:
result.append(root.val)
self.helper(root.left, result)
self.helper(root.right, result)

迭代算法

同理,递归算法使用系统栈,不好控制,性能问题比较严重,需要进一步了解不用递归如何实现。为了维护固定的访问顺序,使用数据结构的先入后出特性

先处理根节点,根据访问顺序根 ➜ 左 ➜ 右,先入栈的后访问,为了保持访问顺序(先入后出),⭐️先把右孩子入栈,再入栈左孩子(此处需要注意,出栈才是访问顺序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if not root: return []

result, stack = [], [root]

while stack:
cur_node = stack.pop() # 访问根节点,直接进行操作(输出到数组)
result.append(cur_node.val)
if cur_node.right: # 先入栈右节点
stack.append(cur_node.right)
if cur_node.left: # 后入栈左节点,这样下一轮循环先访问左节点,维护了访问顺序
stack.append(cur_node.left)

return result

中序遍历

在线刷题:94. Binary Tree Inorder Traversal

递归算法

同理于前序遍历,一模一样的处理方法,考虑访问顺序为左 ➜ 根 ➜ 右即可,快速模仿并写出代码

1
2
3
4
5
6
7
8
class Solution:
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []
return [] if root is None else self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)

同理在这里也附上使用helper()函数的标准写法,代码上来说,只变了名称访问顺序

1
2
3
4
5
6
7
8
9
10
def inorderTraversal1(self, root):
result = []
self.helper(root, result)
return result

def helper(self, root, result):
if root:
self.helper(root.left, result)
result.append(root.val)
self.helper(root.right, result)

迭代算法

核心思路依旧是利用栈维护节点的访问顺序:左 ➜ 根 ➜ 右。使用一个p_node来指向当前访问节点p是代表指针point,另外有一个变量cur_node表示当前正在操作节点(把出栈节点值加入输出数组中),算法步骤如下(可以对照代码注释)

① 访问当前节点,如果当前节点有左孩子,则把它的左孩子都入栈,移动当前节点到左孩子,重复第一步直到当前节点没有左孩子

② 当当前节点没有左孩子时,栈顶节点出栈,加入结果数组

当前节点指向栈顶节点的右节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []
result, stack = [], []

p_node = root # 当前访问节点指针
while p_node or stack:

while p_node: # 把所有当前访问节点的左孩子都入栈
stack.append(p_node)
p_node = p_node.left

cur_node = stack.pop() # 操作栈顶节点,如果是第一次运行到这步,那么这是整棵树的最左节点
result.append(cur_node.val) # 因为已经保证没有左节点,可以访问根节点
if cur_node.right:
p_node = cur_node.right # 将指针指向当前节点的右节点

return result

如果想要精简代码,从逻辑上来看,p_node可以使用root代替,这样写只是为了让代码更可读,和逻辑链相切合,方便理解

后续遍历

在线刷题:145. Binary Tree Postorder Traversal

递归算法

同理先序遍历,代码如下

1
2
3
4
5
6
7
8
class Solution:
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []
return [] if root is None else self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

节省版面,使用helper()函数的写法只需要改变函数名访问顺序

迭代算法 1

后序遍历访问顺序要求为左 ➜ 右 ➜ 根,在对访问节点进行操作的条件是,它的左子树和右子树都已经被访问。这样算法的框架就出来了:只需要对每个节点进行标记,表示这个节点有没有被访问,一个节点能否进行操作的条件就是这个节点的左右节点都被访问过了。

因为栈先入后出,为了维护访问顺序满足条件,入栈顺序应该是根 ➜ 右 ➜ 左(和要求访问顺序相反)。代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []

result, stack = [], [(root, False)]

while stack:
cur_node, visited = stack.pop()
if visited: # 只有访问状态为True的节点才能被操作
result.append(cur_node.val)
else:
stack.append((cur_node, True))
if cur_node.right:
stack.append((cur_node.right, False))
if cur_node.left:
stack.append((cur_node.left, False))

return result

迭代算法 2

还有一种迭代算法利用后序遍历的本身属性,注意到后序遍历的顺序是左 ➜ 右 ➜ 根,那么反序的话,就直接倒序的输出结果,即反后序根 ➜ 右 ➜ 左,和先序遍历的根 ➜ 左 ➜ 右对比,发现只需要稍微改一下代码就可以得到反后序的结果,参考先序遍历,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if root is None: return []

result, stack = [], [root]

while stack:
cur_node = stack.pop()
result.append(cur_node.val)
if cur_node.left: # 修改顺序
stack.append(cur_node.left)
if cur_node.right: # 修改顺序
stack.append(cur_node.right)

return result[::-1] # 反序操作

广度遍历

从上到下的层序102. Binary Tree Level Order Traversal

从下到上的层序(Bottom-up) 107. Binary Tree Level Order Traversal 2

按照层序进行遍历的的过程,有两种说法,一种是按照层序的从顶到底的(level order),另一种是从底到顶的(bottom up),具体实现上来说,就是输出反序即可。在具体问题设计上可能有区别,但是基本思路不变

广度遍历的核心思路就是使用队列,即先进先出 First-in First-out,这里很关键的一点就是以来作为入队和出队的判断条件。并且因为按照层的顺序,是从左到右,所以遍历顺序(入队顺序)为左 ➜ 右

基本思路参看代码注释,逻辑比较简单。实现上,使用Python中的自带类deque来实现,新建为queue = deque([]),入队为queue.append(),出队为queue.popleft()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from collections import deque

class Solution:
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
if root is None: return []
result, queue = [], deque([root])

while queue:
level_len = len(queue) # 记录现在队列中的节点数量
level_nodes = [] # 每层输出
while level_len > 0: # 具体出队入队操作,保证本层所有节点的子节点都入队
cur_node = queue.popleft()
level_nodes.append(cur_node.val)
if cur_node.left:
queue.append(cur_node.left)
if cur_node.right:
queue.append(cur_node.right)
level_len -= 1
result.append(level_nodes)

return result

Brew作者被拒的题

226. Invert Binary Tree,就是一道基本的树的遍历题。有故事说Mac包管理工具Brew的作者Max在Google被面试这道题,没写出来,被拒了。之后Max去了Apple。个人感觉,对于遍历的理解,如果是真的根据逻辑链理解,且对递归有着深刻的理解,那实在不应该写不出这道题,因为真的很简单

题目是这样说的,要求把一颗二叉树的所有左右子树互换位置

递归算法

假设左右子树都搞定了,那么当前节点需要的操作为:把当前节点的左右孩子互换即可,写成递归非常简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
if root is None: return []
# 在本节点的操作,左右孩子互换
root.left, root.right = root.right, root.left
# 已经搞定的左右孩子,使用递归的思路写出函数表达式
self.invertTree(root.right) # 下面两句的顺序并不重要
self.invertTree(root.left)
return root

迭代算法

因为对于每一个节点,只需要把它的左右孩子互换位置,并且依次遍历即可,使用DFSBFS都是一样的,这里用使用栈的深度优先搜索举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def invertTree(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
if root is None: return []
stack = [root]

while stack:
cur_node = stack.pop()
# 对当前节点进行操作
cur_node.left, cur_node.right = cur_node.right, cur_node.left
# 进行入栈操作,保证访问到每一个节点
if cur_node.left: stack.append(cur_node.left)
if cur_node.right: stack.append(cur_node.right)

return root

总结

二叉树遍历问题最关键的逻辑链记忆点如下

  1. 遍历顺序

    ⭐️遍历顺序非常重要,即某 ➜ 某 ➜ 某。如果这一点你不太记得,我认为在考试的过程中可以尝试向面试官确认,的带选项只有三个,就是,全排列也只有6种,长时间不用不记得也是情有可原的。所以在这里非常优秀

  2. 递归 ➜ 假设搞定了

确认遍历顺序后,写出递归方法的核心思维是:⭐️假设左右孩子搞定了(搞定的方式就是调用函数本身,替换自变量即可),现在怎么做才能得到最终答案

  1. 迭代 ➜ 根的位置

一般面试官会继续询问迭代方法如何写,这里的核心思维是:⭐️关注的位置对应的就是出栈输出的操作(在例题中就是添加到输出数组)

那么根据遍历顺序,⭐️只要之前的孩子不为空不能出栈输出,要继续入栈(办法自己想即可,每次可能写出来的代码都不同,但是思路相同。需要例子,可以参考中序后序里的迭代算法部分)

🔲 ☆

TensorSpace 一个3D神经网络可视化框架

【阅读时间】5min | 2k字
【阅读内容】TensorSpace - 是一款3D模型可视化框架,支持多种模型,帮助你可视化层间输出,更直观的展示模型的输入输出,帮助理解模型结构,输出方法。Github

机器学习入门?先上手玩玩看!

是什么(What)

TensorSpace是一款3D模型可视化框架,一动图胜千言。官网链接Github链接

⭐️TensorSpace擅长直观展示模型结构层间数据,生成的模型可交互。官方支持手写字符识别物体识别0-9字符对抗生成网络案例

为什么(Why)

本部分说明:❓为什么要使用这个框架❓这个框架主要解决了什么问题❓我们的灵感来源于何处

3D神经网络可视化一片空白

机器学习可视化上,每个机器学习框架都有自己的【御用工具】,Tensorboard之于TensorflowVisdom之于PytorchMXboard之于MXnet。这些工具的Slogan不一而同的选择了Visualization Learning(TensorBoard的Slogan),也就是面向专业机器学习开发者,针对训练过程,调参等设计的专业向可视化工具

但面向一般的计算机工程师非技术类人才(市场,营销,产品),一片空白,没有一个优秀的工具来帮助他们理解❓机器学习模型到底做了什么,能解决一个什么问题

机器学习开发和工程使用并不是那么遥不可及,TensorSpace搭建桥梁链接实际问题机器学习模型

3D可视化的信息密度更高更直观

市面上常见的机器学习可视化框架都是基于图表(2D),这是由它们的应用领域(训练调试)决定的。但3D可视化不仅能同时表示层间信息,更能直观的展示模型结构,这一点是2D可视化不具备的。例如在何凯明大神的Mask-RCNN论文中:

有这么一幅图来描述模型结构(很多模型设计类和应用落地类的论文都会有这么一副图)而TensorSpace可以让用户使用浏览器方便的构建出一个可交互的神经网络3D结构

更进一步的,利用3D模型的表意能力特点,结合TensorFlow.js,可在浏览器中进行模型预测(跑已经训练好的模型看输入输出分别是什么❗️),帮助理解模型

【模型结构】黑盒子的真面目是什么?

模型就像是一个盛水的容器,而预训练模型就给这个容器装满了水,可以用来解决实际问题。搞明白一个模型的输入是什么,输出是什么,如何转化成我们可理解的数据结构格式(比如输出的是一个物体标识框的左上角左下角目标)就可以方便的理解某个模型具体做了什么

例如,❓Yolo到底是如何算出最后的物体识别框的?❓LeNet是如何做手写识别的?❓ACGAN是怎么一步一步生成一个0-9的图片的?这些都可以在提供的Playground自行探索

如下图所示,模型层间的链接信息可通过直接鼠标悬停具体查看


【层间数据】神经网络的每一层都做了什么?

3D模型不仅仅可以直观的展示出神经网络的结构特征(哪些层相连,每一层的数据和计算是从哪里来),结合Tensorflow.js,可在浏览器中进行模型预测。由于我们已经有了模型结果,所有的层间数据直观可见,如下图所示:

可以通过 TensorSpace Layer的对象属性方便的拿到每一层的输出数据(未经处理),工程和应用上,了解一个模型的原始输出数据方便工程落地

怎么建(How)

首先你需要有一个使用常用框架训练好的的预训练模型,常见的模型都是只有输入输出两个暴露给用户的接口。TensorSpace可以全面的展示层间数据,不过需要用户将模型转换成多输出的模型,过程详见此文档。具体流程如下图所示:

TensorSpace构建对应模型这一步,下面一段构建LeNet的代码可能更加直观,如果要在本地运行,需要Host本地Http Server

你最需要的是模型结构的相关信息TensorflowKeras都有对应的API打印模型结构信息,比如Kerasmodel.summary()。还有类似生成结构图的方式,生成如下图的模型结构2D示意图

是的,你需要对模型结构非常了解才可能构建出对应的TensoSpace模型,未来版本已计划推出自动脚本,通过导入对应的模型预训练文件,一键生成多输出模型。但是TensoSpace的Playground会未来子项目会力所能及的收集更多的模型,在模型应用落地和直观展示这个领域努力做出自己的贡献

谁可能用(Who)

做这样一款开源框架,除了填补3D可视化的一般解决方案的框架空白外,还思索了几个可能可行的应用场景

前端开发者过度机器学习

前端(全栈)开发者,产品经理等

未来,前端的重复性工作可能会慢慢减少。最近有一个原型图 ➜ HTML代码的项目,另一个2017年的开源项目都在尝试利用机器学习自动化一些Coding中的重复劳动,提高效率

机器学习一定不会取代前端工程师,但掌握机器学习工具的工程师会有优势(这种工具会不会整合进Sketch等工具不好说),既然入了工程师行,终身学习势在必行!

TensorSpace虽然不是能帮忙训练和设计模型,但擅长帮助工程师理解已有的模型,找到可应用的领域。并且在接驳光法开发者到机器学习的大道上做了一点微小的工作,走一个可视化的Model Zoo

机器学习教育

机器学习课程教育者

使用Tensorspace直观的在浏览器上显示模型细节和数据流动方向,讲解常见模型的实现原理,比如ResNet,Yolo等,可以让学生更直观的了解一个模型的前世今生,输入什么,输出什么,怎么处理数据等等

我们只是提供了一个框架,每一个模型如果需要直观的展示对数据的处理过程,都值得3D化

模型演示和传播

机器学习开发者

JavaScript最大的优势就是可以在浏览器中运行,没有烦人的依赖,不需要踩过各种坑。有一个版本不那么落后的浏览器和一台性能还成的电脑就可以完整访问所有内容

如果您的项目对展示自己的模型可以做什么是怎么做的有需求,私以为,不应错过TensorSpace

TensorSpace教学模型原理效果非常好。提供了一个接口去写代码,搞清楚每一个输出代表了什么,是如何转化成最后结果(从输出到最后结果的转换还是需要写JavaScript代码去构建模型结构,在这个过程中也能更进一步理解模型的构造细节

现在还没有完成的Yolov2-tiny就是因为JavaScript的轮子较少(大多数处理轮子都使用Python完成),所有的数据处理都需徒手搭建。时间的力量是强大的,我们搭建一个地基,万丈高楼平地起!

致谢

机器学习部分

我们最初的灵感来源于一个真正教会我深度卷积网是如何工作的网站:http://scs.ryerson.ca/~aharley/vis/conv/(源码只能下载,我Host了一份在Github上)

这个网站的效果,也是团队未来努力的方向(大网络上,因为实体过多,性能无法支持。为了解决性能问题,我们优化为:不是一个Pixel一个Pixel的渲染,而是一个特征图一个特征图的处理)

前端部分

使用Tensorflow.js Three.js Tween.js 等框架完成这个项目,感谢前人给的宽阔肩膀让我们有机会去探索更广阔的世界

开发团队们

感谢每一个为这个项目付出的伙伴,没有你们每个人,就没有这个开源项目破土而出!团队中负责开发的syt123450(主力)和Chenhua Zhu,设计师Qi(Nora),感谢他们!还有负责机器学习模型部分和文档的

也欢迎你有什么想法给我留言,或直接在Github上提出Pull Request

🔲 ☆

【直观详解】通俗易懂了解什么是黎曼猜想

【阅读时间】15min - 20min | 9000+字 | 23张动图 | 11张图片
【阅读内容】一文搞懂什么是黎曼猜想。在复数域直观可视化黎曼$\zeta$函数(读作/zita/),解释什么是解析延拓(analytic continuation),探究黎曼猜想和素数的关系

分享者是最大的受益者,感谢各位莅临阅读!

【说明】本文科普为主,是3B1B视频(版权来源)的笔记。会有很多废话来增加信息冗余度,方便对复分析之类概念不熟悉的读者理解。有基础的读者可以选择跳过一些在你们看来是在说废话的内容。就像重要的事情说三遍一样,废话增加信息的冗余度,减慢节奏,降低认知负担,希望这么做后能有更多人真正看懂什么是黎曼猜想

八卦黎曼

黎曼头像

黎曼全名格奥尔格·弗雷德里希·波恩哈德·黎曼 〔德语〕Georg Friedrich Bernhard Riemann(1826 - 1866),德国数学家。黎曼的父亲是个牧师,在他长大之后,就读于哥廷根大学(德国西南部)神学系

后来在大学听了一场高斯有关最小二乘法的讲座,发现了命中天赋所在。征得父亲同意后,转到柏林大学改修数学,拜入雅可比狄利克雷门下(雅克比行列式,第一个玩椭圆的人;机器学习中的狄利克雷分布,证明费马大定理 n=5 的人;没错就是这两个大牛)

历史背景

不谈历史背景的人物介绍都是耍流氓,好,回过头来看看黎曼生长的德国(当时还不能叫德国)当时在干啥

1806年拿破仑覆灭统治几百年的神圣罗马帝国。1815年以日耳曼民族为主的德意志邦联成立,在这个联邦中,以普鲁士奥地利最为强大,两大势力得争个高下。一直到1862年脾斯麦执政,才走向一统。最终,1871年,德意志帝国成立,黎曼没能见到这一天

19世纪5、60年代普鲁士完成了工业革命,这必定伴随着思想和科学的蓬勃发展。黎曼是生在一个好时代,这个时代,整个科学领域,一片蓝海,名家辈出,群星璀璨

有意思的是,K12这个名词就发源于普鲁士K指Kindergarten,及幼儿园,12指12年级,也就是高三。K12泛指基础教育。虽然这工业化的流水线教育模式可能更多的是为了培养流水线式的工业人才或士兵,但不能否认,重视教育则学界兴盛

成就

黎曼成就斐然,最有名的当然是黎曼几何(积分),黎曼流形和复分析之父。当然还有本篇文章的主人公,1859年提出的黎曼猜想

黎曼猜想被收录进1900年希尔伯特(Hilbert)提出的23个重大难题,这些难题经过100年的岁月,还剩下6道没有被完全解决

【补充1】因为代数几何中有关椭圆曲线的相关研究还没有兴盛,著名的费马大定理:$x^n + y^n = z^n \; 当\;n>2\;没有整数解$ 未出现在列表中,虽然当时这个猜想也没有被证明

【补充2】在希尔伯特的问题列表中,黎曼猜想并不单独为一题。包含黎曼猜想的第8题是:黎曼猜想哥德巴赫猜想孪生素数猜想,这每一个猜想都闻名遐迩。三位一体,由此可见这三个问题之间是存在关联的

21世纪黎曼猜想又被列为千禧年7道世纪难题之一,克雷数学研究所承诺:解决一道题 ➜ 100万美元

P/NP问题 | 霍奇猜想 | 庞加莱猜想(已证明)| 黎曼猜想 | 杨-米尔斯存在性与质量间隙 | 纳维-斯托克斯存在性与光滑性 | 贝赫和斯维讷通-戴尔猜想

黎曼猜想的专业定义

先搬运Wiki百科对黎曼猜想的定义:黎曼$\zeta$函数,$\zeta(s) = \frac{1}{1^s} + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \cdots$ 的非平凡零点(在此情况下是指 $s$ 不为-2,-4,-6…等点的值)的实数部分是 $\cfrac{1}{2}$

这个表述可以继续简述为:所有黎曼$\zeta$函数非平凡零点的实数部分是 $\cfrac{1}{2}$

【补充(只为严谨,可以跳过)】把上面的式子称为黎曼$\zeta$函数并不严谨,严谨的来说:定义域必须纳入考虑,才能完整写出黎曼$\zeta$函数的形式

设一复数s,其实数部份 >1 ${s: Re(s) > 1}$ 且
$$
\zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s}
$$

它亦可以用积分定义
$$
\zeta(s) = \frac{1}{\Gamma(s)}\int_{0}^{\infty} \frac{x^{s-1}}{e^x - 1} dx
$$

在区域{s : Re(s) > 1}上,此无穷级数收敛并为一全纯函数。黎曼认识到:$\zeta$ 函数可以通过解析开拓来扩展到一个定义在复数域 $ s, s≠ 1$上的全纯函数 $\zeta(s)$

相信大部分读者如果是第一次看到这个定义,估计头都大了,但是列出不明白概念的清单是学习一个全新事物的有效办法,就按照这个思路来列一个清单

  • ❓ 黎曼$\zeta$函数是个什么函数若读者的数学基础为高中,那么博主猜测您有疑惑的是…这个符号,它的含义很简单:按照1 2 3 4每次加1的规律一直重复前一项的形式。举个例子:

$$
\zeta(s) = \frac{1}{1^s} + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \frac{1}{5^s} + \frac{1}{6^s} + \frac{1}{7^s} + \frac{1}{8^s} + \cdots = \sum_{n=1}^{\infty} \frac{1}{n^s}
$$

  • ❓ 什么是非平凡零点本博文想说清楚的问题
  • 实数部分指的什么?难不成还有虚数部分? 这部分涉及虚数的概念,虚数定义为 $\sqrt{-1}$ 记为符号 i,接下来就需要一些复数基础,这一点跳不过,但本博文也尝试帮你解决这个问题
  • ❓ $\frac{1}{2}$ 这个数是怎么来的? 本博文想说清楚的问题

从知识构建的角度来说,搞清楚黎曼猜想的知识网父节点基础复分析(知道复数以及如何分析)和微积分中的求导

还有另一个说法也和黎曼猜想有关,即 $1 + 2 + 3 + \cdots = - \cfrac{1}{12}$ ,怎么看这个式子都应该是无穷大啊,为啥等于 $-\cfrac{1}{12}$ 呢?看完后,你应该就能明白为什么会有这个令人不解的说法

可视化黎曼$\zeta$函数

第3部分就一步一步展开黎曼猜想这副瑰丽的“画卷”,希望在图穷过程中,能带来给您带来几个Aha时刻,感受数学之美

黎曼$\zeta$函数

首先,我们为了逻辑链的完整,先用一副动图再定义一下我们的主角黎曼$\zeta$函数,并且假设我们带入$s=2$ 会是什么情况

定义

你可以继续带入其他值,如果 $s>1$ ,可算出一个确定的值,但你会发现如果 $s<1$ ,那这个无穷级数(级数就是一长串数字 or 序列的数学专有名词)就会越加越大,无法收敛,又称发散

带入数值

参考上面的动图,带入负数,明显越来越大呀?其实这里和定义域的选取有关,黎曼$\zeta$函数只有在 $s>1$ 的时候能求出值(收敛),这个函数才有意义,那么定义域外的情况怎么处理呢?

如果你对黎曼猜想研究过,可能看过类似的结论 $\zeta(-2n) = 0$ 和 $\zeta(-1) = -\cfrac{1}{12}$ ,这又是为啥呢?

定义域扩展到复数

在传统的定义域中,就是把 $s$ 作为输入带入黎曼$\zeta$函数,重新映射到数轴上的另一个数上,如下面的动图所示。如果你很好奇为什么这个级数的和是 $\cfrac{\pi^2}{6}$,这个数从何而来另一篇博文会解答这个问题(3B1B的另一个视频的总结笔记),我会晚些时候更新

黎曼做了一个扩展,他说:如果 $s$ 能取到复数会是一种什么情况呢?先添加一个复平面,并另 $s=2+i$,过程参看下面的动图

所谓定义域扩展其实非常好理解:之前 $s=1$,它是一个实数。现在让 $s = 2 + i$ ,变成一个复数,这就是复数域扩展

❓这里可能会出现两个问题 ➜ ① 什么是复平面? ② $\left(\cfrac{1}{2}\right)^{2+i}$ 怎么计算?几何含义是什么?

复平面

复数是拥有实部和虚部的表示法,写为 $a + bi$ ,$a$ 为实部,$b$ 为虚部, $\sqrt{-1}$ 定义为 $i$,称为虚数单位 。而复平面(complex plane)是用水平的实轴与垂直的虚轴建立起来的复数的几何表示,如下图所示(来源维基百科)

研究复数有什么意义?其中有一点和我们这个主题有关,$i$ 虚数单位和幂指数函数勾连起来在复分析中能连接上旋转这个概念。具体来说,参看我的这篇【直观详解】让你永远忘不了的傅里叶变换解析博文

虚数单位为幂指数

这一小节的思路非常重要,不仅仅对理解黎曼猜想有帮助,对信号分析,傅里叶变换等也非常有帮助

$2^x$ ,这个 $x$ 就是幂指数,$2^i$ 即虚数单位为幂指数

第二个问题,我们可以把 $\left(\cfrac{1}{2}\right)^{2+i}$ 拆开写成 $\left(\cfrac{1}{2}\right)^{2}× \left(\cfrac{1}{2}\right)^{i}$ ,前面一半很好理解,关键是后面一半怎么理解

这里涉及到一个非常基础并且十分重要的理念:复平面中,纵轴(虚数部分)的幂指数函数的映射关系代表的是旋转。一下子不懂没关系,下面有通过两幅动图来帮助理解,如果还是有疑惑并且十分想了解,参看博文复平面和旋转

首先,下面这副动图表示,假设我们把指数 $i$ 前加一个自变量 $t$ ,就构造了一个函数 $f(t) = \left(\cfrac{1}{2}\right)^{ti}$ ,可以看到左边的黄色点在纵轴(虚轴)上移动,表示的就是引入一个自变量

接着,我们把左边的输入带入 $f(t)$ 得到右边的output像空间,即 $\left(\cfrac{1}{2}\right)^{ti}$ 的值。会有下面一副动图所示的对应关系(移动黄色点,粉色点作为输出联动)。如果改变底数 $\cfrac{1}{2}$ ➜ $\cfrac{1}{9}$ ,左边黄点移动的时候,右边粉点的旋转速度变快,这就是幂指数函数在复数域上的映射规律

总结一下,以上对两个问题的阐述是为了建立一个直观概念:幂指数是虚数单位的乘法对应了复平面内的旋转,接下来一张动图就来看看 $\left( \cfrac{1}{2} \right) ^{2+i}$ 是怎么算的:(关注红色线段,即最后的结果)

① 实部把点收缩到 $\cfrac{1}{4}$ 的位置

② $\left( \cfrac{1}{2}\right)^i$ 不改变长度(因为是虚部),只旋转一个对应的角度

⭐️一步一步可视化

进行旋转

下面这副动图非常重要!先把实部部分加起来,再对每一项还需要乘一个 $(\cfrac{1}{2})^i$ ,也就是每一个线段都需要进行一个同样角度的旋转

这副动图可以多看几遍,应该是挺好理解的。这里有个很细节的问题:第一段线段没动,是不是意味着没有进行旋转呢?,对应相乘的部分是 $1^i$ ,这可能意味着这个旋转恰好是转过了一圈(时间有限,未能求证,大概率是这样理解)

接下来的动图展示了在 $s$ 变化的过程中,对应的螺旋线的变化情况,这幅动图看的有点上瘾

考虑定义域的话,实数部分要能收敛:即在下图右侧黄色高亮区域, $s$ 的实部大于1

变换黎曼$\zeta$函数

理解这个复函数一个好方法是通过变换来将其可视化,即将复函数看作变换,为了加深理解,在变换黎曼$\zeta$函数前,先变换一个比较简单的函数
$$
f(s) = s^2
$$
按照下面的动图,带入 $2$ 得到 $4$ ,带入 $-1$ 得到 $1$ ,带入 $1i$ 得到 $-1$

最后把所有的网格都标记彩色,下一幅动图同时变换网格上所有的点,形成新的网格

同时观察所有点的变换比较吃力,你可以尝试在看的时候关注一个点的变化过程,比如关注 $(-1, 0)$ 这一点:它逆时针旋转了180° 。这副动图给了我们丰富的信息来直观的展现复函数变化到底做了什么

同理,可视化黎曼$\zeta$函数,如下列动图所示

如果这么漂亮的图像都完全无法激起你继续钻研复分析的兴趣,那么……

难受的停顿

你可能已经发现了,变换的图像左边有一个十分突兀的切面,停顿的很不自然,整个图像一场明显的表露除了一种希望冲破定义域的渴望

那么,我们专门高亮两条线:虚部等于 $i$ 和 $-i$ 的两条横线,然后进行变换。难道你没有冲动去补全它吗?

解析延拓

可以去想象,在 $Re(s)=1$ 的左半边,有一个改良版的函数(即下图蓝紫色的函数),可以完美的补全整个空间

用数学的形式来表示的话,问题就变成了,在左边一半的定义域内,这个函数是什么的问题,如下图(Re表示实部)

此时,我们就可以可视化出开篇提到的那个表达式 $\zeta(-1) = 1 + 2 + 3 + \cdots = - \cfrac{1}{12}$ ,你现在知道这个公式怎么来的了,因为钻了定义域不同函数形式不同的空子,其实上面这个式子的论断是很荒谬的

新的问题又出来了,补全的部分,如果没有条件限制,随便怎么画都行,补全这条路难道走不通?

真的是这样吗?并不是,黎曼$\zeta$函数自带一个限制(约束)条件:函数是解析函数,补全部分形式处处可导

这里有一个更加优雅的方法来理解处处可导(解析)这个条件

我们先来看 $f(s) = s^2$ ,它的导数形式是 $f’(s) = 2s$ ,在可视化部分来看,处处可导等价于变换后任意两条线段的夹角不变,又称保角(保证交角不变)特性,参看下图

这个规律在所有网格线中都成立,所以,解析的 = 保持交角不变,即可以把解析的理解成保角的。如果你是一个追根究底的人,就能发现其实还是有例外的,比如在原点的交角变换后呈整数倍的关系,没有保角特性

下面给一副平移的动图,解析函数处处保角

黎曼$\zeta$函数就是一个保角函数,或说解析函数,网格出处垂直,处处可导

因为黎曼$\zeta$函数是一个解析函数,那么要想在左定义域延拓,又要满足解析的性质,有且仅有一种延拓方法,这也是解析延拓的含义

可视化黎曼$\zeta$函数总结

按照解析延拓的方式进行了补全操作后,我们就走完了黎曼$\zeta$函数的可视化过程了,总结一下逻辑链

右定义域内假设自变量为复数,扩展到复数域 ➜ 进行变换,获得复数域可视化形态 ➜ 左定义域内无意义,进行解析延拓

另,这里补充左半边延拓的解析形式,数学家们已经给出了解
$$
\zeta(s) = 2^s{\pi}^{s-1}\sin(\frac{\pi s}{2})\Gamma(1-s)\zeta(1-s)
$$

素数规律和黎曼猜想

再看黎曼猜想

在有了上述的直观理解后,我们再反回来看看黎曼猜想

有了变换的思维后,那么在这个变化后哪些点会落在原点呢?这个问题非常关键,因为它和求黎曼$\zeta$函数的零点等价

首先,所有满足 $Re(s) = -2n$ 的点都会落在原点,这些点被称为平凡零点(根据数学家的传统,他们太容易被发现了,太好理解了,所以被称为“平凡”)

那么非平凡零点呢?我们已知所有的非平凡零点都落在下图的这个临界带(Critical Strip)中。至于原因,如果再仔细看一下这个复数域可视化变换所有点移动的趋势,大概就知道为什么这么说了,简单来说,是复分析变换计算出来的结果

更加令人不可思议的是,这些非平凡零点的具体分布,蕴含着有关素数的海量信息。至于为什么有素数的海量信息,之后会写一篇博文讲讲这其中的奥妙(3B1B的另一个视频的总结笔记)

黎曼猜想就是在说:这些非平凡零点,都在实部 $Re(s)=\cfrac{1}{2}$ 的这条临界线(Critical Line)上,如下图所示。如果它成立,那么它能让我们深刻理解素数分布的规律,根据最新进展中和其他证明,这个规律应该符合某种分布的均匀分布

黎曼猜想

假设在变换过程中高亮 $Re(s)=\cfrac{1}{2}$ 这条线,以我们可以看到的可视化区域(就是动图中前面跳动的一下的部分)的变换过程如下图所示,貌似它并没有过零点?

其实不然,这个动图只绘出了可视区域内的线段的变换结果,如果我们把这个线段加长(不理解可以参考上面的动图,黄色就是可视化时候原图像的线),就得到了下面一个动图了

其中,如果你能证明所有的非平凡零点都在这条临界线上(也就是原命题中的 $\cfrac{1}{2}$),那黎曼猜想变成黎曼定理!同时你也证明了成百上千的现代数学结论,当然,还有100万美元的奖金

有趣的是,现在很多现代数学理论的证明,不管黎曼猜想是正确还是不正确都能被证明是正确的。看到一个叫做littlewood定理的证明就是这样,可算是数学奇妙的冰山一角了

素数规律

之前提及黎曼猜想中蕴含着海量的素数信息。并在开篇有说到,1900年希尔伯特的23大难题中,黎曼猜想哥德巴赫猜想孪生素数猜想同为第8题,寻找素数(分解质因数后只有1和他本身的数),素数分布的规律,质因数分解,还有复分析之间一定是有千丝万缕的联系的

建议大家可以观看李永乐老师的黎曼猜想第二期视频,将素数规律讲的非常好,我这里就当好学生,做一些笔记

素数个数

素数到底有多少个呢?这个问题已经被确定回答了,答案是有无穷多个。那是谁证明的呢?由欧几里得(他是公元前300年的人)证明,使用的是反证法,怎么说的呢?

设质数的个数是有限的,那么就有一个最大的自然数 $p$,可以写成一个素数序列: $2,3,5,\cdots,p$,令
$$
q = 2×3×5×\cdots × p + 1 \tag 1
$$
① 假设 $q$ 是质数 ➜ $q\gt p$ 这和 $p$ 是最大的质数这个假设矛盾

② 假设 $q$ 是合数(不是质数的数) ➜ $q$ 是有约数的,不是1也不是它本身 ➜ 那就一定是(1)式中 $2×3×5×\cdots × p$ 中的某一个,但是由(1)式可得,$q$ 除以$2×3×5×\cdots × p$ 中任何一个数都余1 ➜ 所以肯定不能整除与假设 $q$ 是合数矛盾

证毕。这个证明简洁而优雅,数学之美牛皮

欧拉乘积公式

神人欧拉(1707-1783)出现,推导出了欧拉乘积公式,怎么个说法呢?

假设 $p$ 表示全体素数,有下面一个公式成立
$$
\prod\limits_{p} (1-p^{-s})^{-1} = \frac{1}{1-\cfrac{1}{2^s}}×\frac{1}{1-\cfrac{1}{3^s}}×\frac{1}{1-\cfrac{1}{5^s}}×\cdots = \cfrac{1}{1^s}+\cfrac{1}{2^s}+\cfrac{1}{3^s}+\cdots
$$

那这个公式有什么用呢?它告诉我们,黎曼函数和质数之间有隐含的关系。左边是和所有质数有关的项的乘积,右边是黎曼$\zeta$函数

素数定理

假设有这么一个表达式 $\pi(x)$ 表示小于 $x$ 素数的个数,有这么一个规律,参见下图

啥意思呢,横坐标就是自变量 $x$ 的取值,蓝色的线是 $\cfrac{\pi(x)}{\frac{x}{\ln(x)}}$ ,红色的线是 $\cfrac{\pi(x)}{\int_{2}^{x}\frac{1}{\ln t} dt}$ ,分母的 $\int_{2}^{x}\cfrac{1}{\ln t} dt$ 又被称成 $Li(x)$

可以看到,当 $x \to +\infty $ ,这个 $\pi(x)$ 函数是可以写出表达式的

二号神人高斯(1777-1855)研究了一下关于素数密度 $\rho$ 的问题,也就是1000个数里面,有多少个素数。对的,上面蓝线的规律是高斯最早发现,但当时高斯觉得这个发现貌似并不重要,就没有展开来研究。后来1798年勒让德(1752-1833)发现了下面那个红色曲线的表达式,在学界有个涟漪,高斯在1849年就告诉勒让德,你这不行,是我先发现的啊,所以这公式被称为高斯-勒让德公式
$$
\pi(x) \sim \frac{x}{\ln x} \iff \pi(x) = \int_{0}^{x} \frac{dt}{\ln t} + C
$$
$\sim$ 符号表示趋近于,也就是当 $x \to +\infty $ 的意思。 $C$ 是一个常数,这个常数随着 $x$ 的变大而越来越小

再观察上面的图,明显发现红色的线收敛到1的速度更快,所以后面科勒(1870-1924)做了改进和提高。他说,如果黎曼猜想成立(写到这句话可是真不容易),那么这个关系误差式可更加精确,可大大改善素数定理误差的估计
$$
\pi(x) = Li(x)+ O(\sqrt{x}\ln x)
$$
$O()$ 被称为渐进符号,一般用来描述无穷级数的余项。在计算和表示算法复杂度方面也很用,比如$O(n^2)$ 其实就是忽略 $n$ 一次项和常数项的意思。因为在 $n$ 非常大时, $n$ 一次项对数值的贡献在量级上远小于 $n^2$ 二次项。这个余项的常数项的具体数值还没有算出来

之后50年,这个素数猜想被证明了出来,变成了素数定理。有趣的是,这份证明只是数学家研究黎曼猜想的边角料

素数,自然底数,虚数单位 $i$ 之间一定是存在的一些难以名状的关联,现在看来,有没有可能是量子力学叠加态在数理逻辑推理中的一种巧合的具象模式呢?静待未来,让数学家们给我们一个答案吧。胡适先生说过:哪管它真理无穷,进一寸有一寸的欢喜,切实能感同身受,可能就是这辈子最大的幸事之一了吧?

后记和思考

就在写作这篇博文的过程中,爵士阿蒂亚的证明过程的手稿已经公布(有点存疑是草稿,原文里面竟然有错别字weakly ➜ weekly),公开大会也已经结束,一张PPT证明黎曼猜想,有点诡异

但其中提及黎曼猜想和量子力学的关联给了我一些启发:素数和微观世界的规律一定有某种关联(在级数和等于 $\cfrac{\pi^2}{6}$ 部分的可视化解说里面就有很奇妙的规律关联)

无极生太极,其小无内,其大无外也,两面都是宇宙本源的运行规律,中华民族老祖宗《易经》已经有这种思维方法了,一面是精细结构常数,一面是引力常数

参照弦理论,高维度空间坍缩在很小的尺度内。那能不能猜想,正因为微观世界和高维度尺度更接近,导致被影响的程度也不一样,引力才一直没有统一(而其他三个力的规律已经统一)。相对应的,大尺度上的规律因时间尺度的限制(宇宙的寿命),我们作为人类从观测角度上来说,尺度太小。如果等葛立恒数年后,引力部分也会有一个类似微观世界的规律被发现呢?

这篇文章提到引力常数 $g$ 更加令人疑惑,是不是可能这个常数本身就是由两个量构成?大的那个符合微观规律,细调的那个因为观测受限(尺度太小)我们无法找到佐证的依据?现在人类追求佐证和实验,有没有可能这条路本身就是障碍?

最后还是希望直观详解这个系列能激起更多人的好奇心就心满意足了,附上一份目录

博客目录汇总(更新中)

【参考和来源】

所有动图来自:3B1B的视频【官方双语】黎曼ζ函数与解析延拓的可视化
李永乐老师1+2+3+4+…=-1/12?李永乐老师讲黎曼猜想(1)Youtube
质数有多重要?数学家欧拉和高斯是如何研究质数的 ?李永乐老师讲黎曼猜想(2)
wiki百科黎曼猜想

最后,和3B1B视频一样,来个看完彩蛋:黎曼$\zeta$函数的导数的可视化动图

🔲 ☆

【直观详解】线性代数中的转置正交正规正定

【阅读时间】20min 4589 words
【内容简介】从【直观理解】线性代数的本质笔记出发,继续讨论几个线性代数中的概念,正交,正规,正定及转置的直观解释。旨在能帮助读者在看完后不会忘记什么是正交矩阵,什么是正规矩阵,转置部分进行了深入挖掘,希望找出一些几何直观的解释

在之前的【直观理解】线性代数的本质的笔记中,详细讨论了特征值与特征向量的几何直观意义

起初,研究线性代数,也是因为深入了解矩阵(变换)对机器学习中的很多优美公式的推导和理解有帮助。上篇笔记中,3B1B团队的讲解内容中没有涉及几个线性代数中的概念,且这些概念在做矩阵分解时会被用到。以上一篇笔记中的直观理解为基础(矩阵 = 变换)在这里做一个整理和记录

正交矩阵

可能很多人已经有一个概念:正交(Orthogonal) = 垂直。但我们知道,正交的一定垂直,垂直的不一定正交(比如空间中两个不相交直线垂直)。提及垂直,首先出现你脑海中的特点是什么呢?我想是勾股数 $a^2 + b^2 = c^2$ , 还有 $\cos (\frac {\pi}{2}) = 1$

那什么是正交矩阵呢?在讲这个概念之前,变换中有一种特殊变换:旋转变换。这种变换除了原点外没有特征向量,特征值恒为1,不对网格进行伸缩。或者说,这个变换保证了新列空间内和原列空间内所有对应向量的长度不变

三维情况下,单位矩阵(对角线为1,其他为0,即基向量构成的矩阵)$\mathbf E = \left [ \begin{smallmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0&0&1 \end{smallmatrix} \right ]$ 如下图所示


$\mathbf E$ 中的三个基向量分别记为 $\mathbf {X_a}= \left [ \begin{smallmatrix} 1 \\ 0 \\ 0 \end{smallmatrix} \right ] $ ,$\mathbf {Y_a} = \left [ \begin{smallmatrix} 0 \\ 1 \\ 0 \end{smallmatrix} \right ] $,$\mathbf {Z_a} = \left [ \begin{smallmatrix} 0 \\ 0 \\ 1 \end{smallmatrix} \right ] $ ,用下标a来表示。之后对这个矩阵 $\mathbf E$ 应用一个旋转变换,以 $(0,-0.6,0.8)$ 为旋转轴,转90°。得到三个新的向量,用下标b来表示,记为 $\mathbf {X_b}= \left [ \begin{smallmatrix} 0 \\ 0.8 \\ 0.6 \end{smallmatrix} \right ] $ ,$\mathbf {Y_b} = \left [ \begin{smallmatrix} -0.8 \\ -0.36 \\ 0.48 \end{smallmatrix} \right ] $,$\mathbf {Z_b} = \left [ \begin{smallmatrix} -0.6 \\ 0.48 \\ -0.64 \end{smallmatrix} \right ] $

根据基变换原理,易得旋转变换的矩阵表达式 $\mathbf R = \left [ \begin{smallmatrix} 0&-0.8 &-0.6 \\ 0.8&-0.36&0.48 \\ 0.6&0.48&-0.64 \end{smallmatrix} \right ]$ 计算得特征向量为 $(0,-0.75,1)$,发现这条向量即旋转轴!

此时我们考虑从 $\mathbf R$矩阵下变到 $\mathbf E$的变换矩阵是多少,即求 $\mathbf R$ 矩阵的逆

$$\mathbf R^{-1} = \left [ \begin{matrix} 0 & 0.8 & 0.6 \\-0.8 & -0.36 & 0.48 \\-0.6 & 0.48 & -0.64 \end{matrix}\right]$$

观察形式大家就可以发现一个有趣的特点 $\mathbf R^{-1} = \mathbf R^T$

正交矩阵有一个几何直观的特点,表示一个旋转变换,并且矩阵的逆和矩阵的转置相等

正定与半正定矩阵

根据特征值和特征向量这篇笔记中的内容,我们知道特征值是对一个变换(矩阵)特性的有力表征,公式 $\mathbf A \mathbf{\vec v} = \lambda \mathbf{\vec v}$ 表示了变换中被留在张成空间内的向量就是特征向量的符号表达,其中 $\vec v$ 是特征向量,$\lambda$ 即特征值

我们对上式进行一些数学恒等变换,左乘 $\vec v^T$,得到

$$\vec v^T \mathbf A \vec v = \vec v^T \lambda \vec v = \lambda \vec v^T \vec v \tag{2-1}$$

此时我们会发现一些巧合,先来看看正定矩阵的正规定义:若一个 n×n的矩阵 $\mathbf M$ 是正定的,当且仅当队友所有的非零实系数的向量 $\vec v$,都有 $\vec v^T \mathbf M \vec v > 0$

我们暂时不考虑复数情况(在机器学习预见复数域的内容较少),结合上面的二公式,发现保证 $\vec v^T \mathbf M \vec v > 0$ 即使得 $\lambda \vec v^T \vec v>0$,其中 $\vec v^T \vec v$一定大于等于0(由于 $\vec v$ 是一个1×n的向量,转置进行矩阵相乘实际效果计算元素的平方和),所以可以推出即正定矩阵就是使得特征值大于0

再回到正定矩阵的定义公式 $\vec x^T \mathbf M \vec x > 0$,我们已经有深刻的理解 $\mathbf M \vec x$ 表示对向量 $\vec x$ 进行变换,记变换后的向量为 $\vec y = \mathbf M \vec x$ ,则我们可以把正定矩阵的公式写成

$$\vec x^T \vec y > 0 \tag{2-2}$$

这个公式是不是很熟悉呢?它是两个向量的内积,对于内积,有公式:

$$\cos(\theta) = \frac{\vec x^T \vec y}{\Vert \vec x \Vert * \Vert \vec y \Vert} \hat {\jmath}$$

$\Vert \vec x \Vert \; \Vert \vec y \Vert$ 表示 $\vec x$ 和 $\vec y$的长度,$\theta$ 是它们之间的夹角。根据2-2式,可以得到 $\cos(\theta) > 0$,即它们之间的夹角小于90度

总结:如果说一个矩阵正定,则表示,一个向量经过此矩阵变换后的向量与原向量夹角小于90度

当然,加一个【半】字,是指这个小于变成小于等于

正规矩阵

矩阵中还有一张形状特殊的矩阵,被称为正规矩阵,定义为:如果矩阵 $\mathbf A$ 满足 $\mathbf A^T \mathbf A = \mathbf A \mathbf A^T$

更多的,如果矩阵 $\mathbf U$ 满足 $\mathbf U^T \mathbf U = \mathbf U \mathbf U^T = \mathbf I$,其中 $\mathbf I$ 是单位矩阵,则称矩阵 $\mathbf U$ 为酉矩阵

从变换的角度来看正规矩阵,先做一个变换 $\mathbf A$ 再做一个变换 $\mathbf A^T$。并且交换两个矩阵的位置,最终结果相同

矩阵的转置

什么是转置

在前面的三个描绘矩阵不同矩阵的概念中,多次使用了转置的概念。从矩阵形态的角度看,转置是将 $\mathbf A$ 的所有元素关于一条从第1行第1列元素出发的向右下方45度的射线镜面翻转(下面的动图更加直观)

那么,从矩阵是表示变换的集合角度如何理解转置呢?

为什么转置

试图从另一个角度来理解其实也是为了回答另一个问题:为什么要定义转置这种操作呢?你可能会说,这就是一个【对角线镜像对称交换的操作】,从形式上来理解对一般人已完全足够

这里要深究的原因也只是为了克服在学习机器学习的过程中,公式里若出现转置符号,无法完全理解带来的生涩感(俗称强迫症),对博主来说,一个直接动机源于SVD算法

首先,考虑矩阵的列向量有具体的列空间的含义(对应 $\hat {\imath}$ 和 $\hat {\jmath}$ 的变换位置),若进行转置操作,列空间的性质会被完全破坏,或者说,转换成了一个新的列空间

非方阵

考虑矩阵转置的几何含义是无意义的,或者说,对出现过矩阵转置的公式的进一步理解是没有帮助的

特别的,如果是向量形式(1×n的矩阵),转置很多时候出现,是为了进行二次型运算(即平方运算),设 $\mathbf x = \{x_1, x_2,\ldots, x_n\}$ 是一个1×n的矩阵

很多机器学习的教材中这里会是列矩阵,因为要切合列空间的概念。对于机器学习来说,这里的 $x_1$ 代表的数据的特征维度

计算二次型: $\mathbf x \mathbf x^T = x_1^2 + x_2^2 + \ldots + x_n^2$ ,记过为一个数,表示的是距离

方阵

列空间的概念,转置是一种非常特殊的旋转。这种旋转结合了横向镜面等特性,详细可以参看下图

从这幅图可以看出,如果想从几何变换的角度(类似3b1b的方法)来理解转置相当困难。此时,需要考虑换一种思考角度,性质数学计算(这可能也是解决一个数学问题最常用的两种手段,性质寻找共性,并推广演绎,数学计算导出同样问题的不同表现形式,并总结规律)

性质找规律

首先先用一个简单的例子,二维可视化,并寻找规律,得到下面图像

观察得到,和旋转有一定的关系的,但是其实这个几何意义已经十分抽象了,为了追根之底,从数学的角度进行一些更有意思的探究

数学计算

为了发掘 $\mathbf A$ 和 $\mathbf A^T$ 之间的关系,我们可以设有转换矩阵 $\mathbf T$,其描述了从 $\mathbf A$ 到 $\mathbf A^T$ 的变换,写成公式为:
$$
\mathbf T \mathbf A = \mathbf A^{T} \tag 1
$$
同时我们假设 $\mathbf A$ 是一个2×2的矩阵,如下所示所示(列的不同为字母的不同a和b,行的不同为小标的1和2)

$$\mathbf A= \begin{bmatrix} a_{1} & b_{1} \\ a_{2} & b_{2} \\ \end{bmatrix} \quad \mathbf A^T= \begin{bmatrix} a_{1} & a_{2} \\ b_{1} & b_{2} \\ \end{bmatrix} \tag{2}$$

保证变换不会压缩维度,即 $det(\mathbf A) \neq 0$,利用(1)式,未知数,消元计算后,可以算出 $\mathbf T$,其中 $det(\mathbf A) = a_1b_2 - b_1a_2$

$$\mathbf T = \frac{1}{det(\mathbf A)} \begin{bmatrix} a_{1} b_{2} - a_{2}^2 & a_{1} (a_{2} - b_{1}) \\ b_{2} (b_{1} - a_{2}) & a_{1} b_{2} - b_{1}^2 \\ \end{bmatrix} \tag{3}$$

前面的 $\frac{1}{det(\mathbf A)}$ 作为一个常数,保证了 $det(\mathbf A^{T})=det(\mathbf A)$,将后面关键的变换矩阵写成

$$\mathbf T' = \begin{bmatrix} a_{1} b_{2} - a_{2}^2 & a_{1} (a_{2} - b_{1}) \\ b_{2} (b_{1} - a_{2}) & a_{1} b_{2} - b_{1}^2 \\ \end{bmatrix} \tag {4}$$

把 (4)式进行整理,也写成矩阵相乘的形式,得到下式

$$\mathbf T'=\begin{bmatrix}\begin{bmatrix}a_{1} & a_{2} \\\end{bmatrix} \begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & \begin{bmatrix}a_{1} & a_{2} \\\end{bmatrix} \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix} \\\begin{bmatrix}b_{1} & b_{2} \\\end{bmatrix} \begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & \begin{bmatrix}b_{1} & b_{2} \\\end{bmatrix} \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix}\\\end{bmatrix} \tag{5}$$

对于 $\mathbf A$ 的列空间来说,有 $\mathbf a = \left [ \begin{smallmatrix} a_1 \\ a_2 \end{smallmatrix} \right ]$ 和 $\mathbf b = \left [ \begin{smallmatrix} b_1 \\ b_2 \end{smallmatrix} \right ]$ 观察到 $\mathbf T$ 中包含列空间的两个项,把 $\mathbf T’$ 整理得

$$B'=\begin{bmatrix}{\mathbf a}^{T}\begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & {\mathbf a}^{T} \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix} \\{\mathbf b}^{T} \begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & {\mathbf b}^{T} \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix}\\\end{bmatrix}=\begin{bmatrix}{\mathbf a}\cdot\begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & {\mathbf a}\cdot \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix} \\{\mathbf b}\cdot \begin{bmatrix}b_{2} \\ -a_{2} \\\end{bmatrix} & {\mathbf b}\cdot \begin{bmatrix}-b_{1} \\ a_{1} \\\end{bmatrix}\\\end{bmatrix} \tag{6}$$

令 $\mathbf c = \begin{bmatrix}b_{2} \\ -a_{2} \\ \end{bmatrix} $ $\mathbf d = \begin{bmatrix}-b_{1} \\ a_{1} \\ \end{bmatrix} $ ,观察后发现规律:

  • $\mathbf c$ 变换到 $\mathbf A^T_{\hat {\jmath}}$ 为逆时针旋转90°
  • $\mathbf d$ 变换到 $\mathbf A^T_{\hat {\imath}}$ 为顺时针旋转90°

$\mathbf c$ 和 $\mathbf d$ 组成的列空间设为 $\mathbf C$,写成公式为

$$\mathbf C = \begin{bmatrix}\mathbf c & \mathbf d\end{bmatrix} = \begin{bmatrix} b_2 & -b_1 \\ -a_2 & a_1\end{bmatrix} \tag{7}$$

将(7)式左乘 $\mathbf A$ 得到下式

$$\mathbf A\mathbf C= \begin{bmatrix}a_{1} & b_{1} \\a_{2} & b_{2} \\\end{bmatrix}\cdot\begin{bmatrix}b_{2} & -b_{1} \\-a_{2} & a_{1} \\\end{bmatrix}=\begin{bmatrix}det(\mathbf A) & 0 \\0 & det(\mathbf A) \\\end{bmatrix} =det(\mathbf A) \mathbf I \tag{8}$$

再将(8)式所有乘以 $\mathbf A$ 的逆矩阵 $\mathbf A^{-1}$ 得到
$$
\mathbf C = \mathbf A^{-1} det(\mathbf A) \tag{9}
$$
所以,构造的这个 $\mathbf C$ 矩阵和 $\mathbf A$ 矩阵的逆矩阵有关

另外,(4)式,即 $\mathbf T’$ 矩阵还可以被写成

$$\mathbf T'=\begin{bmatrix}\begin{bmatrix}\begin{bmatrix}a_{1} & b_{1} \\a_{2} & b_{2} \\\end{bmatrix}& \begin{bmatrix}b_{2} \\-a_{2} \\\end{bmatrix}\end{bmatrix}\\\begin{bmatrix}\begin{bmatrix}a_{1} & b_{1} \\a_{2} & b_{2} \\\end{bmatrix}& \begin{bmatrix}-b_{1} \\a_{1} \\\end{bmatrix}\end{bmatrix}\end{bmatrix}=\begin{bmatrix}\begin{bmatrix}\begin{bmatrix}a_{1} & b_{1} \\a_{2} & b_{2} \\\end{bmatrix}& \mathbf c\end{bmatrix}\\\begin{bmatrix}\begin{bmatrix}a_{1} & b_{1} \\a_{2} & b_{2} \\\end{bmatrix}& \mathbf d\end{bmatrix}\end{bmatrix} \tag{10}$$

或者可以写成

$$\begin{align}\mathbf T' & = \begin{bmatrix}\begin{bmatrix}a_{1} & a_{2} \\\end{bmatrix} &\begin{bmatrix}b_{2} & -b_{1} \\-a_{2} & a_{1} \\\end{bmatrix} \\\begin{bmatrix}a_{2} & b_{2} \\\end{bmatrix}&\begin{bmatrix}b_{2} & -b_{1} \\-a_{2} & a_{1} \\\end{bmatrix}\\\end{bmatrix} = \begin{bmatrix}\mathbf a^{T}&\begin{bmatrix}\mathbf c & \mathbf d \\\end{bmatrix} \\\mathbf b^{T}&\begin{bmatrix}\mathbf c & \mathbf d \\\end{bmatrix}\\\end{bmatrix}\\ & = \begin{bmatrix}\mathbf c^{T}\mathbf C \\\mathbf d^{T}\mathbf C\\\end{bmatrix}=det(\mathbf A) \begin{bmatrix}\mathbf a^{T}\mathbf A^{-1}\\\mathbf b^{T}\mathbf A^{-1}\\\end{bmatrix} \end{align} \tag{11}$$

由 $\mathbf T \mathbf A = \mathbf A^T$ 可得

$$\mathbf T= \begin{bmatrix} \mathbf a^T \mathbf A^{-1} \\ \mathbf b^T \mathbf A^{-1} \end{bmatrix} \tag{12}$$

这时候,可以得出一个结论,矩阵的转置的过程和矩阵的逆是有关系的,是矩阵逆的一个更加复杂的表现形式

有了这个作为基础,考虑一下具有对称性,构造 $\mathbf A \mathbf A^T$,这个复合变换有着很好的对称性和分解性(接下来为了方便,默认非粗体表示的矩阵变换),因为

$$AA^T = \begin{bmatrix} a_1 & b_1 \\ a_2 & b_2\end{bmatrix}\begin{bmatrix} a_1 & a_2 \\ b_1 & b_2\end{bmatrix} = \begin{bmatrix} a_1^2 + b_1^2 & a_1a_2+b_1b_2 \\ a_1a_2+b_1b_2 & a_2^2 + b_2^2\end{bmatrix} \tag{13}$$

考虑任何不进行压缩维度变换的矩阵都可以进行特征分解,则有

$$AA^{T} = R_{AA^{T}} \Lambda_{AA^{T}} (R^{-1})_{AA^{T}} \tag{14}$$

之后左乘(这里补充一下,所有的左乘操作在几何意义上来说,就是附加了一个新的变换)$A^{-1}$,得到

$$A^{T} = A^{-1} R_{AA^{T}} \Lambda_{AA^{T}} (R^{-1})_{AA^{T}} \tag{15}$$

根据之前(1)式的例子进行计算,发现 $R_{AA^{T}} = (R^{-1})_{AA^{T}}$,并且 $R_{AA^T}$ 首先将空间关于 y 轴对称,之后旋转 $\alpha$ 角度,所以假设定义

$$R_{AA^{T}}^{'} = \begin{bmatrix}cos \alpha & -sin \alpha \\sin \alpha & cos \alpha \\\end{bmatrix} \tag{16}$$

利用上面的推导,带入(15)式,得到:

$$A^{T}=A^{-1} R_{AA^{T}}^{'} \begin{bmatrix}-1 & 0 \\0 & 1 \\\end{bmatrix}\Lambda_{AA^{T}} R_{AA^{T}}^{'}\begin{bmatrix}-1 & 0 \\0 & 1 \\\end{bmatrix} \tag{17}$$

用更加清晰的符号改写(17)式得到

$$A^{T}=A^{-1} R_{\alpha} M_y \Lambda R_{\alpha} M_y \tag{18}$$

$M_y$ 表示的是关于y轴对称
$R_\alpha$ 表示逆时针旋转 $\alpha$ 度
$\Lambda$ 表示一种伸缩变换

这里的(18式)结论和上面的作图规律,还有(12)式从某种程度上来说有相似的地方

其实应该从矩阵分解的部分来再次思索矩阵转置的意义,可详见什么是PCA,SVD

最终感觉关于转置还是研究的不够透彻,可能需要拔高到另一个层面去理解会更加直观,但是限于水平,只能至此(并不是数学专业的学生)

总结

  • $\mathbf A \mathbf A^T$ 的对称特性非常有用
  • 正交 = 旋转 = 垂直,并且逆等于转置
  • 正规矩阵的定义和转置息息相关,但是从形式上来看,约束条件是逐步变弱的,其实这些特征都是描述了空间一个变换的一些变化模式,比如旋转,伸缩的特殊模式,只需要有一个基本的直观理解,在机器学习领域就已经足够用了
🔲 ⭐

【直观详解】什么是PCA、SVD

【阅读时间】
【内容简介】

在说明一个解释型内容的过程中,我一直坚信,带有思考的重复的是获取的知识的唯一捷径,所以会加入很多括号的内容,即另一种说法(从不同角度或其他称呼等),这样有助于理解。加粗的地方我也认为是比较重要的关键字或者逻辑推导,学习有一个途径就是划重点,做笔记。

What & Why PCA(主成分分析)

PCA,Principal components analyses,主成分分析。广泛应用于降维,有损数据压缩,特征提取和数据可视化。也被称为Karhunen-Loeve变换

降维的方法角度来看,有两种PCA的定义方式,这里需要有一个直观的理解:什么是变换(线性代数基础),想整理一下自己线性代数的可以移步我的另一篇文章:【直观详解】线性代数的本质

但是总的来说,PCA的核心目的是寻找一个方向(找到这个方向意味着二维中的点可以被压缩到一条直线上,即降维),这个方向可以:

  • 最大化正交投影后数据的方差(让数据在经过变换后更加分散
往低维度的投影直观表示图

紫色的直线 $u_1$ 即是关于 ${x_1,x_2}$ 二维的正交投影的对应一维表示
PCA定义为使绿色点集的方差最小(方差是尽量让绿色所有点都聚在一坨
其中的蓝线是原始数据集(红点)到低纬度的距离,这可以引出第二种定义方式

  • 最小化投影造成的损失(下图中所有红线(投影造成的损失)加起来最小)
投影造成的损失
投影造成的损失

PCA 主成分分析主要目的是为了减少数据维数,其中Auto-encoder也是一种精巧的降维手段

What & Why SVD(奇异值分解)

SVD,Singular Value Decomposition,奇异值分解。最直观的解释如下图所示

往低维度的投影直观表示图

我们知道,矩阵描述的是一种变换(如果对这个概念有疑惑的,欢迎移步我的博客笔记:线性代数的本质)奇异值分解是矩阵分解的其中一种。换句话说,从上图的圆变换为右边的椭圆,通过一个 $\mathbf M$ 矩阵就可以做到,但是,我们知道,非方阵是很不好处理的,我们希望,可以把 $\mathbf M$ 矩阵表示的变换,分解为其他几种变换的组合(注意,分解之后,被分解的分量包含 $\mathbf M$ 的信息,我们可以使用这些分量来进行操作),这几个变换我们希望是方阵,或者有特殊的性质。

$$\mathbf M = \mathbf U \cdot \mathbf \Sigma \cdot \mathbf V^*$$

$\mathbf M$ 是一个m×n阶矩阵(输入为n维向量,输出为m维向量

$\mathbf U$ 的列组成一套基向量,m×m阶矩阵,为$\mathbf M \mathbf M^*$ 的特征向量

$\mathbf \Sigma$ 对角矩阵,对角线上的值称为奇异值,可视为在输入与输出之间进行的标量的“伸缩尺度控制”。为 $\mathbf M \mathbf M^*$ 或 $\mathbf M^* \mathbf M$ 的非零特征值的平方根

$\mathbf V^*$ 是 $\mathbf V$ 的共轭转置(实数域即 $\mathbf V^T$),n×n阶矩阵,$\mathbf V$ 的列组成一套基向量,为 $\mathbf M^* \mathbf M$ 的特征向量

这里我们发现这个 $\mathbf U$ 还有 $\mathbf V$ 都是方阵,恰好满足之前的需求

且有 $\mathbf U \mathbf U^T = \mathbf I_n$ 同时 $\mathbf V \mathbf V^T = \mathbf I_m$ ,所以 $\mathbf U$ 和 $\mathbf V$ 是正交矩阵,而我们知道,正交矩阵对应的变换,就是旋转变换

对于 $\mathbf \Sigma$ 来说,我们知道特征值就是表示的度量伸缩程度的因子,即上图中的伸缩压缩程度(图中很直观的体现了这一点)

总结,SVD就是把一个非方阵(压缩变换)分解为一个旋转➜伸缩压缩➜旋转三个变换(矩阵),如上图所示

How PCA

🔲 ☆

【直观详解】什么是正则化

【阅读时间】7min - 9min
【内容简介】主要解决什么是正则化,为什么使用正则化,如何实现正则化,外加一些对范数的直观理解并进行知识整理以供查阅

Why & What 正则化

我们总会在各种地方遇到正则化这个看起来很难理解的名词,其实它并没有那么高冷,是很好理解的

首先,从使用正则化解决了一个什么问题的角度来看:正则化是为了防止过拟合, 进而增强泛化能力。用白话文转义,泛化误差(generalization error)= 测试误差(test error),其实就是使用训练数据训练的模型在测试集上的表现(或说性能 performance)好不好

过拟合

如上图,红色这条“想象力”过于丰富上下横跳的曲线就是过拟合情形。结合上图和正则化的英文 Regularizaiton-Regular-Regularize,直译应该是:规则化(加个“化”字变动词,自豪一下中文还是强)。什么是规则?你妈喊你6点前回家吃饭,这就是规则,一个限制。同理,在这里,规则化就是说给需要训练的目标函数加上一些规则(限制),让他们不要自我膨胀。正则化,看起来,挺不好理解的,追其根源,还是“正则”这两字在中文中实在没有一个直观的对应,如果能翻译成规则化,更好理解。但我们一定要明白,搞学术,概念名词的准确是十分重要,对于一个重要唯一确定的概念,为它安上一个不会产生歧义的名词是必须的,正则化的名称没毛病,只是从如何理解的角度,要灵活和类比。

我的思考模式的中心有一个理念:每一个概念被定义就是为了去解决一个实际问题(问Why&What),接着寻找解决问题的方法(问How),这个“方法”在计算机领域被称为“算法”(非常多的人在研究)。我们无法真正衡量到底是提出问题重要,还是解决问题重要,但我们可以从不同的解决问题的角度来思考问题。一方面,重复以加深印象。另一方面,具有多角度的视野,能让我们获得更多的灵感,真正做到链接并健壮自己的知识图谱

How 线性模型角度

对于线性模型来说,无论是Logistic Regression、SVM或是简单的线性模型,都有一个基函数 $\phi()$,其中有很多 $\mathbf w$ (参数)需要通过对损失函数 $E()$ 求极小值(或最大似然估计)来确定,求的过程,也就是使用训练集的训练过程:梯度下降到最小值点。最终,找到最合适的 $\mathbf w$ 确定模型。从这个角度来看,正则化是怎么做的呢?

二次正则项

我们看一个线性的损失函数(真实值和预测值的误差)
$$E(\mathbf w) =\frac{1}{2} \sum_{n=1}^{N}\{t_n-\mathbf w^T \phi (\mathbf x_n)\}^2 \tag{1}$$

$E(\mathbf w)$ 是损失函数(又称误差函数)E即Evaluate,有时候写成L即Loss
$t_n$ 是测试集的真实输出,又称目标变量【对应第一幅图中的蓝色点】
$\mathbf w$ 是权重(需要训练的部分,未知数)
$\phi()$ 是基函数,例如多项式函数,核函数
测试样本有n个数据
整个函数直观解释就是误差方差和,$\frac{1}{2}$ 只是为了求导后消去方便计算

正则化项,得到最终的误差函数(Error function)
$$\frac{1}{2} \sum_{n=1}^{N}\{t_n-\mathbf w^T \phi (\mathbf x_n)\}^2 + \frac{\lambda}{2} \mathbf w^T \mathbf w \tag{2}$$

(2)式被称为目标函数(评价函数)= 误差函数(损失函数) + 正则化项
$\lambda$ 被称为正则化系数,越大,这个限制越强

2式对 $\mathbf w$ 求导,并令为0(使误差最小),可以解得

$$\mathbf w = (\lambda \mathbf I + \Phi^T \Phi)^{-1}\Phi^T\mathbf t$$

这是最小二乘法的解形式,所以在题目中写的是从“最小二乘角度”。至于为何正则化项是 $\frac{\lambda}{2} \mathbf w^T \mathbf w$ 在之后马上解释

一般正则项

直观的详解为什么要选择二次正则项。首先,需要从一般推特例,然后分析特例情况的互相优劣条件,可洞若观火。一般正则项是以下公式的形式

$$\frac{1}{2} \sum_{n=1}^{N}\{t_n-\mathbf w^T \phi (\mathbf x_n)\}^2 + \frac{\lambda}{2} \sum_{j=1}^{M} {\vert w_j \vert}^q \tag{3}$$

M是模型的阶次(表现形式是数据的维度),比如M=2,就是一个平面(二维)内的点

q=2就是二次正则项。高维度没有图像表征非常难以理解,那就使用二维作为特例来理解。这里令M=2,即 $\mathbf x =\{x_1,x_2\} \;\mathbf w=\{w_1,w_2\}$ ,令q=0.5 q=1 q=2 q=4

正则项的边缘直观表示

横坐标是$w_1$
纵坐标是$w_2$
绿线是等高线的其中一条,换言之是一个俯视图,而z轴代表的是 $ \frac{\lambda}{2} \sum_{j=1}^{M} {\vert w_j \vert}^q$ 的值

空间想象力不足无法理解的读者希望下方的三维图像能给你一个直观的领悟(与绿线图一一对应)

正则项的边缘直观表示

q=2是一个圆非常好理解,考虑 $z = w_1^2 + w_2^2 $ 就是抛物面,俯视图是一个圆。其他几项同理(必须强调俯视图和等高线的概念,z轴表示的是正则项项的值)

正则项的边缘直观表示

蓝色的圆圈表示没有经过限制的损失函数在寻找最小值过程中,$\mathbf w$的不断迭代(随最小二乘法,最终目的还是使损失函数最小)变化情况,表示的方法是等高线,z轴的值就是 $E(\mathbf w)$
$w^*$ 最小值取到的点

可以直观的理解为(帮助理解正则化),我们的目标函数(误差函数)就是求蓝圈+红圈的和的最小值(回想等高线的概念并参照3式),而这个值通在很多情况下是两个曲面相交的地方

可以看到二次正则项的优势,处处可导,方便计算,限制模型的复杂度,即 $\mathbf w$ 中M的大小,M是模型的阶次M越大意味着需要决定的权重越多,所以模型越复杂。在多项式模型多,直观理解是每一个不同幂次的 $x$ 前的系数,0(或很小的值)越多,模型越简单。这就从数学角度解释了,为什么正则化(规则化)可以限制模型的复杂度,进而避免过拟合

不知道有没有人发现一次正则项的优势,$w^*$ 的位置恰好是 $w_1=0$ 的位置,意味着从另一种角度来说,使用一次正则项可以降低维度(降低模型复杂度,防止过拟合)二次正则项也做到了这一点,但是一次正则项做的更加彻底,更稀疏。不幸的是,一次正则项有拐点,不是处处可微,给计算带来了难度,很多厉害的论文都是巧妙的使用了一次正则项写出来的,效果十分强大

How 神经网络模型角度

我们已经知道,最简单的单层神经网,可以实现简单的线性模型。而多隐含层的神经网络模型如何来实现正则化?(毕竟神经网络模型没有目标函数)

M表示单层神经网中隐含层中的神经元的数量

上图展示了神经网络模型过拟合的直观表示

我们可以通过一系列的推导得知,未来保持神经网络的一致性(即输出的值不能被尺缩变换,或平移变换),在线性模型中的加入正则项无法奏效

所以我们只能通过建立验证集(Validation Set),拉网搜索来确定M的取值(迭代停止的时间),又称为【提前停止】

这里有一个尾巴,即神经网络的不变量(invariance),我们并不希望加入正则项后出现不在掌控范围内的变化(即所谓图像还是那个图像,不能乱变)。而机器学习的其中一个核心目的也是去寻找不同事物(对象)的中包含信息的这个不变量(特征)。卷积神经网络从结构上恰恰实现了这种不变性,这也是它强大的一个原因

范数

我并不是数学专业的学生,但是我发现在讲完线性模型角度后,有几个概念可以很轻松的解答,就在这里献丑把它们串联起来,并做一些总结以供查阅和对照。

我们知道,范数(norm)的概念来源于泛函分析与测度理论,wiki中的定义相当简单明了:范数是具有“长度”概念的函数,用于衡量一个矢量的大小(测量矢量的测度)

我们常说测度测度,测量长度,也就是为了表征这个长度。而如何表达“长度”这个概念也是不同的,也就对应了不同的范数,本质上说,还是观察问题的方式和角度不同,比如那个经典问题,为什么矩形的面积是长乘以宽?这背后的关键是欧式空间的平移不变性,换句话说,就是面积和长成正比,所以才有这个

没有测度论就没有(现代)概率论。而概率论也是整个机器学习学科的基石之一。测度就像尺子,由于测量对象不同,我们需要直尺量布匹、皮尺量身披、卷尺量房间、游标卡尺量工件等等。注意,“尺子”与刻度(寸、米等)是两回事,不能混淆。

范数分为向量范数(二维坐标系)和矩阵范数(多维空间,一般化表达),如果不希望太数学化的解释,那么可以直观的理解为:0-范数:向量中非零元素的数量;1-范数:向量的元素的绝对值;2-范数:是通常意义上的模(距离)

向量范数

关于向量范数,先再把这个图放着,让大家体会到构建知识图谱并串联知识间的本质(根)联系的好处

正则项的边缘直观表示

p-范数

$$\Vert\mathbf x \Vert_p =(\sum\limits_{i=1}^{N}\vert x_i \vert^p)^{\frac{1}{p}}$$

向量元素绝对值的p次方和的 $\frac{1}{p}$ 次幂。可以敏捷的发现,这个p和之前的q从是一个东西,随着p越大,等高线图越接近正方形(正无穷范数);越小,曲线弯曲越接近原点(负无穷范数)

而之前已经说明,q的含义是一般化正则项的幂指数,也就是我们常说的2范数,两者在形式上是完全等同的。结合范数的定义,我们可以解释一般化正则项为一种对待求参数 $\mathbf w$ 的测度,可以用来限制模型不至于过于复杂

$-\infty$-范数

$$\Vert \mathbf x \Vert_{-\infty} = arg \operatorname*{min}_{i}{\vert x_i \vert}$$

所有向量元素中绝对值的最小值

1-范数

$$\Vert \mathbf x \Vert_1 = \sum\limits_{i=1}^{N}\vert x_i \vert$$

向量元素绝对值之和,也称街区距离(city-block)

$$\begin{matrix}4 & 3 & 2 & 3 & 4 \\3 & 2 & 1 & 2 & 3\\2 & 1 & 0 & 1 & 2 \\3 & 2 & 1 & 2 &3 \\4&3 & 2 &3 &4 \\\end{matrix}$$

2-范数

$\Vert\mathbf x \Vert_2 = \sqrt{\sum\limits_{i=1}^{N} x_i^2}$ :向量元素的平方和再开方Euclid范数,也称欧几里得范数,欧氏距离

$$\begin{matrix}2.8&2.2&2&2.2&2.8 \\2.2&1.4&1&1.4&2.2 \\2&1&0&1&2 \\2.2&1.4&1&1.4&2.2 \\2.8&2.2&2&2.2&2.8 \\\end{matrix}$$

$\infty$-范数

$\Vert \mathbf x \Vert_{\infty} = arg \operatorname*{max}_{i}{\vert x_i \vert}$ :所有向量元素中绝对值的最大值,也称棋盘距离(chessboard),切比雪夫距离

$$\begin{matrix}2 & 3 & 2 & 2 & 2 \\2 & 1 & 1 & 1 & 2\\2 & 1 & 0 & 1 & 2 \\2 & 1 & 1 & 1 &2 \\2&2 & 2 &2 &2 \\\end{matrix}$$

矩阵范数

1-范数

$$\Vert \mathbf A \Vert_{1} = arg \operatorname*{max}_{1 \leqslant j \leqslant n}\sum\limits_{i=1}^m{\vert a_{i,j} \vert}$$

列和范数,即所有矩阵列向量绝对值之和的最大值

$\infty$-范数

$$\Vert \mathbf A \Vert_{\infty} = arg \operatorname*{max}_{1 \leqslant i \leqslant n}\sum\limits_{j=1}^m{\vert a_{i,j} \vert}$$

行和范数,即所有矩阵行向量绝对值之和的最大值

2-范数

$\Vert \mathbf A \Vert_{2} = \sqrt{\lambda_{max}(\mathbf A^* \mathbf A) }$

p=2m=n方阵时,称为谱范数。矩阵 $\mathbf A$ 的谱范数是 $\mathbf A$ 最大的奇异值或半正定矩阵 $\mathbf A^T \mathbf A$ 的最大特征值的平方根

$\mathbf A^*$ 为 $\mathbf A$ 的共轭转置,实数域等同于 $\mathbf A^T$

F-范数

$\Vert \mathbf A \Vert_{F} = \sqrt{ \sum\limits_{i=1}^m \sum\limits_{j=1}^n \vert a_{i,j}\vert^2 }$

Frobenius范数(希尔伯特-施密特范数,这个称呼只在希尔伯特空间),即矩阵元素绝对值的平方和再开平方

核范数

$\Vert \mathbf A \Vert_{*} = \sum\limits_{i=1}^n \lambda_i$ :$\lambda_i$ 若 $\mathbf A$ 矩阵是方阵,称为本征值。若不是方阵,称为奇异值,即奇异值/本征值之和

总结

相信每个人在学习过程中都有过看书时,遇到0-范数正则化,或者1-范数正则化,2-范数正则化的表达时很迷惑。写到这里,希望大家能对这些看起来无法理解的晦涩名词有一个融会贯通的理解和感知!

Learning with intuitive and get Insight

以上!鞠躬!

🔲 ⭐

Pandas-Wiki

【阅读时间】百科类
【内容简介】pandas函数库相关操作手册,供查阅使用。笔记对象来自集智

必备库的导入

1
2
3
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

创建对象

创建一个Series对象:传递一个数值列表作为参数,令Pandas使用默认索引。

1
2
3
4
5
6
7
8
9
10
s = pd.Series([1,3,5,np.nan,6,8])
print(s)
>
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64

创建一个DataFrame对象:传递待datetime索引和列标签的Numpy数组作为参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 首先创建一个时间序列
dates = pd.date_range('20130101', periods=6)
print(dates)

# 创建DataFrame对象,指定index和columns标签
df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
print(df)
>
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
A B C D
2013-01-01 0.194508 -0.897507 0.224745 0.090260
2013-01-02 2.412146 -1.191852 -1.644737 0.190971
2013-01-03 -0.674645 0.395960 1.425822 -0.718231
2013-01-04 0.349046 0.315026 2.058357 -0.882345
2013-01-05 1.467093 0.146932 -0.680309 -0.519155
2013-01-06 2.223284 -0.305247 -0.559043 1.017710

也可以传递词典来构建DataFrame,该词典的元素是形如Series的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
df2 = pd.DataFrame({ 'A' : 1., 'B' : pd.Timestamp('20130102'), 'C' : pd.Series(1,index=list(range(4)),dtype='float32'), 'D' : np.array([3] * 4,dtype='int32'), 'E' : pd.Categorical(["test","train","test","train"]), 'F' : 'foo' })

print(df2)

# 查看df2对象各列的数据类型
print(df2.dtypes)
>
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo
A float64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object

观察数据

查看一个DataFrame对象的前几行和最后几行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print(df.head())
print(df.tail(3))
# 默认情况下,.head()和.tail()输出首尾的前5行,也可以手动指定输出行数。
>
A B C D
2013-01-01 0.194508 -0.897507 0.224745 0.090260
2013-01-02 2.412146 -1.191852 -1.644737 0.190971
2013-01-03 -0.674645 0.395960 1.425822 -0.718231
2013-01-04 0.349046 0.315026 2.058357 -0.882345
2013-01-05 1.467093 0.146932 -0.680309 -0.519155
A B C D
2013-01-04 0.349046 0.315026 2.058357 -0.882345
2013-01-05 1.467093 0.146932 -0.680309 -0.519155
2013-01-06 2.223284 -0.305247 -0.559043 1.017710

查看索引(index),列标签(columns)和Numpy数组形式的内容。 .describe()返回简约的统计信息,在工程中非常实用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 索引
print(df.index)
>
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04','2013-01-05', '2013-01-06'],dtype='datetime64[ns]', freq='D')

# 列标签
print(df.columns)
>
Index(['A', 'B', 'C', 'D'], dtype='object')

# 数值
print(df.values)
>
[[ 0.19450849 -0.89750748 0.22474509 0.09025968]
[ 2.41214612 -1.19185153 -1.64473716 0.19097097]
[-0.67464536 0.39595956 1.42582221 -0.71823105]
[ 0.3490457 0.31502599 2.05835699 -0.88234524]
[ 1.46709286 0.14693219 -0.68030862 -0.51915519]
[ 2.2232837 -0.30524727 -0.55904285 1.01771006]]

# 统计
print(df.describe())
>
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.995239 -0.256115 0.137473 -0.136798
std 1.231715 0.663815 1.391936 0.711615
min -0.674645 -1.191852 -1.644737 -0.882345
25% 0.233143 -0.749442 -0.649992 -0.668462
50% 0.908069 -0.079158 -0.167149 -0.214448
75% 2.034236 0.273003 1.125553 0.165793
max 2.412146 0.395960 2.058357 1.017710

灵活调教你的DataFrame:转置与排序

1
2
3
4
5
6
7
8
# 转置
print(df.T)

# 按轴排序,逐列递减
print(df.sort_index(axis=1, ascending=False))

# 按值排序,'B'列逐行递增
print(df.sort_values(by='B'))

选中

尽管标准Python/Numpy的选中表达式很直观也很适合互动,但在生产代码中还是推荐使用Pandas里的方法如.at,.iat,.loc,.iloc,.ix等。

获取

DataFrame里选中的一列与Series等效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
print(df["A"]) # 与df.A相同
>
2013-01-01 0.194508
2013-01-02 2.412146
2013-01-03 -0.674645
2013-01-04 0.349046
2013-01-05 1.467093
2013-01-06 2.223284
Freq: D, Name: A, dtype: float64

# 用[]分割DataFrame
print(df[0:3])
print(df['20130102':'20130104'])
>
A B C D
2013-01-01 0.194508 -0.897507 0.224745 0.090260
2013-01-02 2.412146 -1.191852 -1.644737 0.190971
2013-01-03 -0.674645 0.395960 1.425822 -0.718231
A B C D
2013-01-02 2.412146 -1.191852 -1.644737 0.190971
2013-01-03 -0.674645 0.395960 1.425822 -0.718231
2013-01-04 0.349046 0.315026 2.058357 -0.882345

按标签选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 选中一整行
print(df.loc[dates[0]])
>
A 0.194508
B -0.897507
C 0.224745
D 0.090260
Name: 2013-01-01 00:00:00, dtype: float64

# 按标签选中复数列(所有行,输出只显示前5行)
print(df.loc[:,['A','B']])
>
A B
2013-01-01 0.194508 -0.897507
2013-01-02 2.412146 -1.191852
2013-01-03 -0.674645 0.395960
2013-01-04 0.349046 0.315026
2013-01-05 1.467093 0.146932
2013-01-06 2.223284 -0.305247

# 行/列同时划分(包括起止点)
print(df.loc['20130102':'20130104',['A','B']])
>
A B
2013-01-02 2.412146 -1.191852
2013-01-03 -0.674645 0.395960
2013-01-04 0.349046 0.315026

# 返回一个元素(两个方法等效)
print(df.loc[dates[0],'A'])
print(df.at[dates[0],'A'])
>
0.194508494402
0.194508494402

按位置选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 位置索引为3的行(从0开始,所以其实是第4行)
print(df.iloc[3])
>
A 0.349046
B 0.315026
C 2.058357
D -0.882345
Name: 2013-01-04 00:00:00, dtype: float64

# 按位置索引分割DataFrame
print(df.iloc[3:5,0:2])
print(df.iloc[[1,2,4],[0,2]])
>
A B
2013-01-04 0.349046 0.315026
2013-01-05 1.467093 0.146932

# 直接定位一个特定元素
df.iloc[1,1]
df.iat[1,1]
>
A C
2013-01-02 2.412146 -1.644737
2013-01-03 -0.674645 1.425822
2013-01-05 1.467093 -0.680309

布尔值索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 用一列的值来选择数据
print(df.A > 0)
>
2013-01-01 True
2013-01-02 True
2013-01-03 False
2013-01-04 True
2013-01-05 True
2013-01-06 True
Freq: D, Name: A, dtype: bool

# 使用.isin()函数过滤数据
df2 = df.copy()
df2['E'] = ['one', 'one','two','three','four','three']

# 提取df2中'E'值属于['two', 'four']的行
print(df2[df2['E'].isin(['two','four'])])
>
A B C D E
2013-01-03 -0.674645 0.395960 1.425822 -0.718231 two
2013-01-05 1.467093 0.146932 -0.680309 -0.519155 four

设置

赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 为DataFrame创建一个新的列,其值为时间顺序(与df相同)的索引值
s1 = pd.Series([1,2,3,4,5,6], index=pd.date_range('20130102', periods=6))
print(s1)

df['F'] = s1

# 按标签赋值
df.at[dates[0],'A'] = 0

# 按索引赋值
df.iat[0,1] = 0

# 用Numpy数组赋值
df.loc[:,'D'] = np.array([5] * len(df))

# 最终结果
print(df)

缺失数据

Pandas默认使用np.nan来代表缺失数据。Reindexing允许用户对某一轴上的索引改/增/删,并返回数据的副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建DataFrame对象df1,以dates[0:4]为索引,在df的基础上再加一个新的列'E'(初始均为NaN)
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E'])

# 将'E'列的前两个行设为1
df1.loc[dates[0]:dates[1],'E'] = 1

print(df1)
>
A B C D E
2013-01-01 0.194508 -0.897507 0.224745 0.090260 1.0
2013-01-02 2.412146 -1.191852 -1.644737 0.190971 1.0
2013-01-03 -0.674645 0.395960 1.425822 -0.718231 NaN
2013-01-04 0.349046 0.315026 2.058357 -0.882345 NaN

处理缺失数据

1
2
3
4
5
6
7
8
# 剔除df1中含NaN的行(只要任一一列为NaN就算)
df1.dropna(how='any')

# 用5填充df1里的缺失值
df1.fillna(value=5)

# 判断df1中的值是否为缺失数据,返回True/False
pd.isnull(df1)

操作

统计

此类操作默认排除缺失数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 求平均值
print(df.mean())
>
A -0.190821
B -0.050040
C -0.203207
D 5.000000
F 3.000000
dtype: float64

# 指定求平均值的轴
print(df.mean(1))
>
2013-01-01 1.264749
2013-01-02 1.049748
2013-01-03 1.578067
2013-01-04 1.035639
2013-01-05 1.855754
2013-01-06 1.936110
Freq: D, dtype: float64

# 创建Series对象s,以dates为索引并平移2个位置
s = pd.Series([1,3,5,np.nan,6,8], index=dates).shift(2)
print(s)
>
2013-01-01 NaN
2013-01-02 NaN
2013-01-03 1.0
2013-01-04 3.0
2013-01-05 5.0
2013-01-06 NaN
Freq: D, dtype: float64

# 从df中逐列减去s(若有NaN则得NaN)
print(df.sub(s, axis='index'))
>
A B C D F
2013-01-01 NaN NaN NaN NaN NaN
2013-01-02 NaN NaN NaN NaN NaN
2013-01-03 -1.572312 0.570952 -1.108305 4.0 1.0
2013-01-04 -3.401496 -4.304842 -4.115467 2.0 0.0
2013-01-05 -4.955735 -5.576715 -4.188780 0.0 -1.0
2013-01-06 NaN NaN NaN NaN NaN

应用

DataFrame中的数据应用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 逐行累加
print(df.apply(np.cumsum))
>
A B C D F
2013-01-01 0.000000 0.000000 0.058997 5 NaN
2013-01-02 0.277465 -0.161767 -0.807960 10 1.0
2013-01-03 -0.294847 1.409186 -0.916265 15 3.0
2013-01-04 -0.696343 0.104344 -2.031732 20 6.0
2013-01-05 -0.652078 -0.472372 -1.220512 25 10.0
2013-01-06 -1.144929 -0.300242 -1.219241 30 15.0

# 每列的最大值减最小值
print(df.apply(lambda x: x.max() - x.min()))
>
A 0.849776
B 2.875794
C 1.926687
D 0.000000
F 4.000000
dtype: float64

字符

Series对象的str属性具有一系列字符处理方法,可以很轻松地操作数组的每个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 字符串变为小写
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
print(s.str.lower())
>
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object

合并

连接

Pandas在join/merge两中情境下提供了支持多种方式,基于逻辑/集合运算和代数运算来连接Series,DataFrame和Panel对象。

concat()方法连接数组

1
2
3
4
5
6
7
8
9
df = pd.DataFrame(np.random.randn(10, 4))
print(df)
print("------")

# 拆分成块
pieces = [df[:3], df[3:7], df[7:]]

# 重新连接,可得初始数组
print(pd.concat(pieces))

增补

给DataFrame增补行

1
2
3
4
5
6
7
df = pd.DataFrame(np.random.randn(8, 4), columns=['A','B','C','D'])
print(df)
print("------")

# 将索引为3的行增补到整个DataFrame最后
s = df.iloc[3]
print(df.append(s, ignore_index=True))

组合

“组合”包含以下步骤:

  • 基于一定标准将数据分组
  • 对每个部分分别应用函数
  • 把结果汇合到数据结构中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 新建DataFrame对象df
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'], 'B' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'C' : np.random.randn(8), 'D' : np.random.randn(8)})

print(df)
>
A B C D
0 foo one 0.298545 -0.101893
1 bar one 1.080680 -0.717276
2 foo two 1.365395 0.939482
3 bar three 0.783108 -0.575995
4 foo two -1.089990 -0.033826
5 bar two 0.442084 1.533146
6 foo one 0.041715 0.190613
7 foo three 0.529231 0.380723

# 对'A'列进行合并并应用.sum()函数
print(df.groupby('A').sum())
>
C D
A
bar 2.305871 0.239875
foo 1.144897 1.375099

# 对'A', 'B'两列分别合并形成层级结构,再应用.sum()函数
print(df.groupby(['A','B']).sum())
>
C D
A B
bar one 1.080680 -0.717276
three 0.783108 -0.575995
two 0.442084 1.533146
foo one 0.340260 0.088720
three 0.529231 0.380723
two 0.275406 0.905656

重塑

层次化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
tuples = list(zip(*[['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]))

# 多重索引
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])

df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
df2 = df[:4]

print(df2)
>
A B
first second
bar one -1.144920 -0.823033
two 0.250615 -1.423107
baz one 0.291435 -1.580619
two -0.574831 -0.742291

# .stack()方法将DataFrame的列“压缩”了一级
stacked = df2.stack()
print(stacked)
>
first second
bar one A -1.144920
B -0.823033
two A 0.250615
B -1.423107
baz one A 0.291435
B -1.580619
two A -0.574831
B -0.742291
dtype: float64

对于已经层次化的,具有多重索引的DataFrame或Series,stack()的逆操作是unstack()——默认将最后一级“去层次化”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
print(stacked.unstack())
>
A B
first second
bar one -1.144920 -0.823033
two 0.250615 -1.423107
baz one 0.291435 -1.580619
two -0.574831 -0.742291

print(stacked.unstack(1))
>
second one two
first
bar A -1.144920 0.250615
B -0.823033 -1.423107
baz A 0.291435 -0.574831
B -1.580619 -0.742291

print(stacked.unstack(0))
>
first bar baz
second
one A -1.144920 0.291435
B -0.823033 -1.580619
two A 0.250615 -0.574831
B -1.423107 -0.742291

数据透视表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3, 'B' : ['A', 'B', 'C'] * 4, 'C' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2, 'D' : np.random.randn(12), 'E' : np.random.randn(12)})

print(df)
>
A B C D E
0 one A foo -0.411674 0.284523
1 one B foo -1.217944 1.519293
2 two C foo 0.502824 -0.167898
3 three A bar 0.565186 0.226860
4 one B bar 0.626023 0.401529
5 one C bar -0.437217 0.832881
6 two A foo -0.825128 0.346303
7 three B foo 0.069236 0.728729
8 one C foo 1.647690 -0.531091
9 one A bar -0.881553 0.070718
10 two B bar 0.203672 1.601761
11 three C bar 1.334214 -0.778639

# 生成数据透视表
print(pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C']))
>
C bar foo
A B
one A -0.881553 -0.411674
B 0.626023 -1.217944
C -0.437217 1.647690
three A 0.565186 NaN
B NaN 0.069236
C 1.334214 NaN
two A NaN -0.825128
B 0.203672 NaN
C NaN 0.502824

时间序列

Pandas提供了简单、强力且有效的工具,可以在频率转换中进行重采样(比如从秒级数据中提取5分钟一更新的数据)。这在金融工程中应用甚广,当然也不仅限于金融领域。

时区表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
rng = pd.date_range('3/6/2012 00:00', periods=5, freq='D')
ts = pd.Series(np.random.randn(len(rng)), rng)
print(ts)
>
2012-03-06 -0.605179
2012-03-07 0.961252
2012-03-08 1.309406
2012-03-09 1.184313
2012-03-10 0.249745
Freq: D, dtype: float64

# 设定国际时区标准
ts_utc = ts.tz_localize('UTC')
print(ts_utc)
>
2012-03-06 00:00:00+00:00 -0.605179
2012-03-07 00:00:00+00:00 0.961252
2012-03-08 00:00:00+00:00 1.309406
2012-03-09 00:00:00+00:00 1.184313
2012-03-10 00:00:00+00:00 0.249745
Freq: D, dtype: float64

# 切换时区
print(ts_utc.tz_convert('US/Eastern'))
>
2012-03-05 19:00:00-05:00 -0.605179
2012-03-06 19:00:00-05:00 0.961252
2012-03-07 19:00:00-05:00 1.309406
2012-03-08 19:00:00-05:00 1.184313
2012-03-09 19:00:00-05:00 0.249745
Freq: D, dtype: float64

周期和时间戳之间的转换让我们可以方便的使用一些算术运算。比如下面的例子,我们把一个以季度为单位的时间序列转换为按日期表示

1
2
3
4
5
6
7
prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')

ts = pd.Series(np.random.randn(len(prng)), prng)
print(ts.head())

ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
print(ts.head())

分类

1
2
3
4
5
df = pd.DataFrame({"id":[1,2,3,4,5,6], "raw_grade":['a', 'b', 'b', 'a', 'a', 'e']})
# 将原始记录转换为分类类型
df["grade"] = df["raw_grade"].astype("category")

print(df["grade"])

将类别重命名为更有意义的字样

1
2
3
4
5
6
7
8
9
10
11
12
df["grade"].cat.categories = ["very good", "good", "very bad"]

# 重排类别同时添加上缺失的类别名称
df["grade"] = df["grade"].cat.set_categories(["very bad", "bad", "medium", "good", "very good"])

print(df["grade"])

# 排序在各分类中分别进行
print(df.sort_values(by="grade"))

# 对类别列分组可以显示空类
print(df.groupby("grade").size())

绘图

随机累加序列

1
2
3
4
5
6
7
8
# 生成一串随机序列
ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))

# 求累加和
ts = ts.cumsum()

# 输出图象
ts.plot()

绘制带标签的列

1
2
3
4
5
# 生成一个4列的DataFrame,每列1000行,并逐列累加
df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=['A', 'B', 'C', 'D'])
df = df.cumsum()

df.plot(); plt.legend(loc='best')

获取数据输入/输出

CSV

df是一个DataFrame

1
2
3
4
5
# 输出至.csv文件
df.to_csv('haha.csv')

# 从.csv文件中读取数据
pd.read_csv('haha.csv')

Excel

df是一个DataFrame

1
2
3
4
5
6
# 输出至.xlsx文件
df.to_excel('haha.xlsx', sheet_name='Sheet1')

# 从.xlsx文件中读取数据
pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA'])
('haha.csv')
🔲 ☆

【区块链】比特币与金融、ICO和监管

【阅读时间】13min - 17min - 4187 words
【内容简介】其中讨论了区块链技术(比特币)对金融体系的影响,并解读了最近很火的热点问题:什么是ICO,对一些大事件的时间表进行记录和总结

三个关键问题

英格兰银行副行长,布罗德本特提出了有关区块链金融的三个关键问题十分犀利,也为宋老师推崇,这里列出来大家共同思考

第一个问题

比特币它并不是创造了一种新的货币,而是创造了一种新的清算方式

清算权

清算问题始终是中央银行诞生的最重要的动力,是金融体系中相当核心的问题,而货币发行权的基础在于清算权

  • 17世纪,荷兰的阿姆斯特丹银行,进化成了中央银行,首创用金银币的账户来进行清算
  • 19世纪,上海汇丰银行。控制了大清国的货币清算功能。担当清算中心的职责,整个清朝内部的资金流动,都要通过汇丰银行
  • 到现在,美国对某某国家采取金融制裁,因为美国掌握着美元的清算权
  • 支付宝和微信支付争的是网络交易的清算权

谁能控制清算权,就占据了战略的制高点,有俯瞰全局的作用,控制战局。清算权意味着游戏规则的制订权

这种新的清算方式,成本低。英格兰银行希望主导这种潮流,也就是RSCoin的核心

  • 英国的数字货币,依赖大账本进行清算
  • 但是它要控制这个大账本

比特币清算的致命缺陷

  • 互联人没有信用体系,用消耗运算力的方式来保持整个记账体系的安全
  • 浪费巨大的资源,导致记账效率低下
  • 会出现记账效率的瓶颈。比如,比特币,每秒只能完成7笔交易,而全世界体系要求的记账效率非常高,比如,VISA卡,每秒2000 - 7000笔

RSCoin的解决办法

记账效率低下,是由于竞争记账。既然银行控制大账本,没有必要搞竞争,指定少数几家银行,负责记账。双层记账体系,树形结构优化,RSCoin使用30个授权节点,一秒可以达到2000笔

我的观点

有关英国人弄得RSCoin来讲,个人认为已经脱离的了区块链技术的核心Idea:贪婪=信任。既然还是有中心化的控制,那么就等同于只使用了区块链的数据结构,本质来说,也就不是加密货币

但是,有一些博弈和妥协的想法是非常值得借鉴的,比如,流水账本身就有透明的好处,其实是从另一方面利用区块链密码学加密技术实现了一种变种的信息透明

第二个问题

如果数字货币是一个独立的货币,未来它将会跟传统的银行,争夺储蓄

举个例子来说,共享单车想当于数字货币。出现之前,单车控制权都是银行。骑车,只能从一家银行停车场到另一家银行的停车场。自行车的控制权,由银行转向了最终客户。只要能解开密码锁,你就可以随便骑,随意停。久而久之,共享单车数量越来越大,传统的自行车就会越来越小

银行的商业模式是:吸纳储蓄➜放贷➜吃存款和贷款之间的利差。数字货币的量越大,银行体系中的储蓄减少,金融体系面临挤兑的风险,数字货币很可能成为传统银行这种商业模式的掘墓人,如何解决这个问题,布罗德本特提出了第三个问题

第三个问题

新的数字货币如何去倒逼传统银行商业模式的改革 (胖银行➜瘦银行)

胖银行的基本运行模式

  • 货币金融学中的概念:部分准备金:允许银行用一块钱的准备金进行10倍的放大

    ,即用1块钱创造10块钱

  • 银行体系因为这个制度有了巨大的竞争优势。开银行等于印钞

  • 其中的前提条件或基础就是一定要获得大量的储蓄然后才能进行放贷

  • 存在问题

    • 储蓄是别人的钱,是短期的,放贷的资产是长期的
    • 在金融危机中,资产会迅速贬值,使得银行体系变得不稳定
    • 危机爆发,银行倒闭,银行倒闭,社会流动性紧缩,钱荒,更多银行倒闭,工厂关门,工人下岗,消费力消失,导致金融危机和经济危机
  • 特权化是社会动荡的罪魁祸首

瘦银行

亚当·斯密,李嘉图等主张搞瘦银行(不赞同部分准备金制度) ,比如有名的芝加哥计划

周期性金融危机的爆发,就跟银行不断放大这种杠杆就直接的关系。其中的关键问题是:如果银行由胖变瘦,使银行总的资产负债表收缩,社会的流动性紧缩,发生钱荒,经济就会停滞不前

解决方式

  • 央行给全体国民每个人开账户
  • 传统意义上,只有金融机构可以在中央银行开账户,目的也是相互清算。以前做不到,主要原因是技术水平不够,效率太低,成本太高
  • 但在数字货币时代,这个问题很简单。意味着央行向人民开放资产负债表,人人可以在央行有账户。应对金融危机的方式变成,中央银行给每个国民的账户上打钱

重资产

商业银行现在是重资产

  • 持有大量的贷款,长期贷款。承当巨大的风险:比如利率,汇率不稳定;经济周期;地缘政治冲突;国际油价。进而引发贷款出现违约
  • 在日益动荡的社会中,重资产的运行模式,风险大,利差小

轻资产

转型问题,就涉及到轻资产,这里可以用万达的转型之路来举例

  • 最开始重资产:从拿地,融资,投资,建设到经营,自己搞定。造成的问题是人多事杂;押占大量资金;销售也有压力
  • 转型成轻资产模式:出品牌,设计广场,钱由社会上投资人来投 ➜负责运营,利润分成➜赚取管理费用

银行也是类似的轻资产模式

  • 银行出品牌,出技术团队:因为银行的人对全国各种项目比较了解,知道哪个贷款比较可靠,风险低
  • 重点工作是:设计贷款产品;设计完,投资人来出钱,银行负责风控,运营,挣的钱也是服务费

关于三个问题的思考

这么一套总结下来,的确想象中十分美好,但是我们不能忽略国家间的政治博弈,利益冲突。银行转型,也是涉及到了众多的问题和利益方面的权衡和Trade-off,但我认为,区块链比特币的出现,的确给了银行一定的压力,这样就和布罗德本特先生的真知灼见切合了,是一种倒逼,是一种时代潮流倾轧的必然产物

金融结算应用

结算步骤

  • 交易指令管理(包括交易验证)
  • 清算(即计算交易双方的财务义务)
  • 结算(即最终的资产转移)

CSDs功能

中央证券登记机构(Central Security Depositories,CSDs)在结算中发挥了关键作用,承担了3项主要功能

  • 认证(公正并受信任地维护已发行证券的记录)
  • 结算(将证券的所有权从卖方转给买方)
  • 账目维护(建立并更新证券的所有权记录)

中央证券登记机构还承担证券托管资产服务融资报告证券出借等功能

结算行业的痛点

在金融交易后结算中,一笔交易涉及多个中介机构。每个中介机构都使用自己的系统来处理、发送和接收交易指令、核对数据、管理差错等,并维护自己的交易记录。每个中介机构使用的数据标也准都不统一

这些都会产生大量成本区块链技术有助于建立并维护共享的、同时化(synchronized)的账目,简化交易对账过程。

目前,结算行业讨论在金融交易后结算中引入私有的、有准入限制的区块链系统。其中,每一个节点扮演不同角色并且在读取区块链上信息上有不同权限,一组受信任的参与者承担验证职能

区块链优势

  • 通过分布式、同时化、共享的证券所有权记录来简化和自动化交易后结算工作,降低交易对账和数据管理成本
  • 缩短结算所需时间,减低结算风险敞口
  • 因为与交易有关信息由交易双方共享,能促进自动清算
  • 缩短托管链,使投资者可以直接持有证券,降低投资者承担的法律和运营风险以及中介成本
  • 可跟踪性好,透明度高
  • 去中心化、多备份能提高系统安全性和抗压性

应用难点

  • 如何实现认证功能

    尽管区块链能保证分布式账本的准确性,但还需有一个受信任的机构来确保已发行证券信息的真实性,一定的中心化必须的,只是可以利用计算机和分布式存储大数据的方式来提高效率,这部分是非常靠谱的

  • 如何实现存管功能

    特别是,如何将托管机构和存管机构持有的资产转移到区块链上。一个可行方案是使用电子凭证(digital token)来代表不在区块链上的资产,但需要一个受信任机构来确保电子凭证与资产之间的对应关系

  • 如何实现券款对付(Delivery versus payment,DvP)?

    这要求区块链能同时处理现金账户

  • 如何确保结算的最终性(settlement finality)?

    比如,比特币区块链系统因为分叉的存在,只能在概率意义上确保结算的最终性(尽管该概率随时间趋向1)

  • 在法律上,区块链上的记录能否构成所有权证明

  • 交易匹配和差错管理。区块链在比较不同维度数据、处理合同不匹配和例外情况上还面临不少障碍

  • 如何在多方参与验证的情况下,确保交易信息的保密性

    一个方案是一个受信任的机构和交易双方才能参加与交易有关的共识机制

    另一个方案是区分交易数据和验证所需数据。零知识验证(zero-knowledge proofs, ZKPs)也是一个可能的解决工具

  • 身份管理

  • 可扩展性

  • 与现有流程和基础设施的兼容性(interoperability)

区块链在产权登记、确认、交易等中的应用逻辑和有待解决的问题,与金融交易后结算非常类似。在这个领域,仔细琢磨,还有很多难关需要攻克

什么是ICO

最近中国强势出手,关停ICO,手段雷厉风行,很多ICO项目都变成了一场庞氏骗局,让人唏嘘,这里针对ICO做一些思考,出自借鉴科普文章

赌场例子

  • 有一个人开了一间赌场,每个赌徒要来赌场玩,必须先换一些筹码,才能参与赌场内的赌局。赌场内所有赌局都是实时利用筹码结算的。
  • 这间赌场服务特别好,赌具特别好,入场费收得也非常低,总之一切都特别好,于是越来越多的赌徒都慕名而来,跑到这间赌场来玩,赌局额度也越来越大。自然,这就需要更多的筹码来确保赌场的顺利运营。
  • 但是,这间赌场的筹码是用一种特殊的金属、特殊的工艺制造的,这些赌具只能用这种筹码玩。赌场一开始的时候造了一万个,以后再也造不出来了。怎么办?
  • 参与赌局的人越来越多,赌局额度越来越大,原本1个筹码1美元,同时100个人在玩,一次赢1美元,赌场运行得很顺利,除了在赌徒间流通的筹码外,赌场手里还有一些筹码,可以卖给刚进场的赌徒。现在来了1000个人,每次要赢100美元,依然只有10000个筹码
  • 为了玩得尽兴,于是赌徒之间互相约定,我们1个筹码100美元,这样就能玩得尽兴了。新进场的赌徒从老赌徒手里高价买回了筹码,老赌徒大赚了一笔,新赌徒玩的尽兴。赌场呢?手里剩的筹码也可以100美元的价格卖出去了
  • 由于赌场运营得越来越好,来的赌徒越来越多,赌局越来越大,筹码的价格就一路上涨
  • 对于赌场而言,由于一开始有大量的筹码以1美元的价格卖给了老赌徒,如果按照100美元的价格承兑,赌场就亏大了。于是,赌场就宣布,找我承兑筹码可以,但是只能按照1美元的价格
  • 但是赌场在卖出筹码的时候,依然按照市场价100美元出售,这也成为赌场盈利的一种手段。于是大家都不会把赢来的筹码拿去找赌场退钱,而是留在手里等着卖给新的赌徒

赌场例子背后的经济学常识

随着生产力越来越高,市场越来越大,市场上流通的商品(总价值)越来越多,如果货币的流通速度不变,理论上就需要更多的货币。从赌场故事中,可以非常容易地看出,由于赌场固定了筹码的总额,加上很多人持币等待升值造成筹码流通速度变化不大,于是筹码价格只能上涨。

进行一个赌场ICO

  • 又有一个人,看到了赌场的生意这么好,眼红了,于是他也想开一间赌场。可是他没有足够的本钱,怎么办呢?于是他公开宣布:我要开一间赌场,赌场服务非常好,赌局非常好,手续费非常低。我的赌场只有10000个筹码,以后绝不增加
  • 现在筹码先造出来了,我把其中5000个筹码拿出来,任何人都可以来买。这5000个筹码卖完了以后,我就用卖掉的钱作为本钱开赌场。等赌场开业了,你们可以把手里的筹码卖给赌徒,或者自己来赌都行。
  • 由于开始赌场还没开业,所以出售的筹码价格也是很低的,0.5美元1个。等赌场开业了,至少1美元1个,甚至更高。由于很多人都看好这个生意,于是5000个筹码很快就卖光了。这个人筹集到了2500美元,造了一些赌具,租了间房子,赌场就开始营业了。
  • 由于他的服务真的很好,很快又聚集了大量的赌徒,于是筹码的价格一路飞涨,很快就到了100美元。在赌场开业前以0.5美元的价格买了筹码的人,筹码增值了200倍。

我们看到,其中加粗的环节都是预先设计或者是预想的。并且这个案例和加密货币(区块链)的特别不谋而合

ICO英文全称是Initial Coin Offering,翻译成中文是“初始货币供应”。

ICO的基本原理

  • 公司创造了一种商业模式,在这种商业模式里,大家只能使用公司自己发行的“代币”(虚拟货币)进行交易。公司事先宣布,这种代币总额是固定,或者增发的方式是固定,也就是说,任何人都不能更改代币增发的规则(区块链技术从本质上保证了这一点)
  • 如果大家认为我们的商业模式非常有前途,我们的代币就会增值。现在公司拿出一定比例的代币进行发售,用筹得的经费作为本钱来运营这种商业模式。
  • 这种依靠出售日后商业模式中的某种公司产品(如果代币可以视为公司产品的话)的方式来筹集资金的金融行为,就被称为ICO

ICO的特点

  • 1、所发行的代币必须是在未来的商业模式中有使用价值,并且不可替代。
  • 2、ICO的商业模式中,代币的发行方式必须事先固定规则,并且不可更改。
  • 3、ICO虽然是一种商业融资的方式,但是ICO模式并不出让股权或者负债,也就是说,运营该商业的公司,未来仍可以继续出让股权或者举债

ICO与区块链

如果你之前已经对区块链最大的核心思想了有了了解,你就知道ICO里面有一个C coin,本身就是看中了区块链没有任何一个中心能够控制这个系统,数据一旦产生便不可更改,并通过工作量证明,产生的强大信任

但是,这个加密货币的实体在未来到底能不能变现,必须要落到实际问题中,真真正正可以解决人们生活中的实际问题。

加密货币直接能解决问题是金融体系的一些问题,这在布罗德本特的三个问题中已经进行了详细的阐述。正因为金融体系的局限性,这种货币不可能同时出现很多并且都具有那么高的价值。

那就需要以太坊这种本质上的服务类落地的想法来晚上这一落地过程,这部分的一些创新和面临的技术难题,在如何评估竞争币的价值与智能合约中我进行了一切探讨

数字加密货币监管

监管事件

  • 【2017年8月】中国严打数字加密货币交易所和ICO
  • 【2018年1月】中国政府开始叫停数字加密货币的场外交易和“出海转内销”
  • 日本对加密货币交易非常宽容,甚至有一定大财团入场的情况发生
  • 【2017年1月】Coinbase上线比特币现金交易功能,造成流动性溢价
  • 【2018年1月】中国政府要求境内挖矿企业有序退出,一些“矿池”准备搬到加拿大

市场操纵

现阶段,40%的比特币被1000个以下的地址持有,代币过于集中,出现市场操纵,不说做空和对冲,典型的简单手法即:若干代币持有大户一起达成共识,提高代币价格,吸引散户投资者进入,然后大户集中减持。俗称【割韭菜】

违法地带

加密货币从一出现开始,就是黑市的青睐交易手段,数字加密货币被用来洗钱,绕开资本管制,逃税,赌博等

总结

加密货币在现阶段不可能取代法币(货币)的地位。人与人之间就有固定的地位差距,贫富差距,天赋差距,运气差距都有固定的隔阂,本身去中心化的分布式存储系统:区块链,并不能真正的做到全世界范围脱离政府,脱离权利中心的私有财产神圣不可侵犯

我作为一个学习CS专业,学习机器学习的人来说,对于金融的理解十分浅显,希望各位前辈能多多指教,共同探讨!

以上!鞠躬!

【参考】

与区块链和数字货币有关的思考

🔲 ☆

【直观详解】支持向量机SVM

【阅读时间】13min - 19min
【内容简介】详解解读什么是支持向量机,如何解支持向量以及涉及的拉普拉斯乘子法,还有核方法的解读

什么是支持向量机-SVM

支持向量机-SVM(Support Vector Machine)从本质来说是一种:用一条线(方程)分类两种事物

SVM的任务是找到这条分界线使得它到两边的margin都最大,注意,这里的横坐标是 $x_1$ 纵坐标为 $x_2$,如下图所示

margin
margin

有了直观的感知,在定义这一节在做一些深入的思考,分解名词(Support Vector Machine)并尝试解释:

  • Machine - Classification Machine 说明它的本质是一个分类器
  • Support Vector - 如上图所示,在Maximum Margin上的这些点就是支持向量,具体说即最终分类器表达式中只含有这些支持向量的信息,而与其他数据点无关。在下面的公式中,只有支持向量的系数 $\alpha_i$ 不等于0。说人话,上图中两个红色的点,一个蓝色的点,合起来就是支持向量$$\mathbf w \cdot \varphi (\mathbf x) = \sum_i \lambda_i y_i k(\mathbf x_i,\mathbf x)$$

公式中每一个符号的含义在后文有说明

如何求解支持向量机

对于我们需要求解的这个超平面(直线)来说,我们知道

  • 它离两边一样远(待分类的两个部分的样本点)
  • 最近的距离就是到支持向量中的点的距离

根据这两点,抽象SVM的直接表达(Directly Representation)

注:$arg \operatorname*{max}_{x} f(x)$ 表示当 $f(x)$ 取最大值时,x的取值

$$arg \operatorname*{max}_{boundary} margin(boundary) \\ \text{所有正确归类的两类到boundary的距离} \ge margin \tag{1}$$

其实这个公式是一点也不抽象,需要更进一步的用符号来表达。

我们知道在准确描述世界运行的规律这件事上,数学比文字要准确并且无歧义的多,文字(例子)直观啰嗦,数学(公式)准确简介

硬间隔

SVM支持向量机
SVM支持向量机

注:公式中加粗或者带有向量箭头的都表达一个向量

  • 假设这些数据线性可分,也可称为硬间隔(Hard Margin)
  • 首先定义超平面:$\mathbf w^T \vec x_i + b = 0$,接下来为了方便,设 $\vec x = (x_1,x_2)$ 即一条直线
  • 任意点 $\vec x_i$ 到该直线的距离为 $\frac{1}{\lVert \mathbf w \lVert} (\mathbf w^T \vec x_i + b)$
  • 对于空间内所有训练点的坐标记为 $(\vec x_i,y_i)$,其中 $y_i$ = 1 or -1, 表示点 $\vec x_i$ 所属的类
  • 如果这些训练数据是线性可分的,选出两条直线(上图中的虚线),使得他们的距离尽可能的大,这两条直线的中央就是待求的超平面(直线)
  • 为了表达直观,我们定义这两个超平面(直线)分别为 $\mathbf w^T \vec x_i + b = 1$ 和 $\mathbf w^T \vec x_i + b = -1$,两个超平面(直线)之间的距离为 $\gamma = \frac{2}{\lVert \mathbf w \lVert}$

    注:选择1的好处是,wb进行尺缩变换(kwkb)不改变距离,方便计算

  • 为了使得所有样本数据都在间隔区(两条虚线)以外,我们需要保证对于所有的 $i$ 满足下列的条件

    • $\mathbf w^T \vec x_i + b \geqslant 1$ 若 $y_i = 1$
    • $\mathbf w^T \vec x_i + b \leqslant -1$ 若 $y_i = -1$
  • 上述两个条件可以写作 $y_i(\mathbf w^T \vec x_i + b) \geqslant 1, \;\text{for all 1}\; 1\leqslant i \leqslant n$ 这里的n指样本点的数量
  • 上面的表达(Directly Representation)可以被写成

    $$arg \operatorname*{max}_{\mathbf w,b} \left\{ {\frac{1}{\lVert \mathbf w \lVert} \operatorname*{min}_{n} [y_i(\mathbf w^T\vec x_i}+b)]\right\} \tag{2}$$
  • 最终目的是找到具有“最大间隔”(Maximum Margin)的划分超平面(直线),找到参数 $\mathbf w$ 和 $b$ 使得 $\gamma$ 最大

  • 则可以对(2)式进行形式变换,得到 canonical representation$$arg \operatorname*{max}_{\mathbf w,b} \frac{2}{\lVert \mathbf w \lVert} \implies arg \operatorname*{min}_{\mathbf w,b} \frac{1}{2}\lVert \mathbf w \lVert ^2 \\ s.t.\; y_i(\mathbf w^T\vec x_i+b) \geqslant1,\;i = 1,2,\ldots,m \tag{3}$$

注:s.t. :subject to 表示约束条件,表达的意思等价于:为了使得所有样本数据都在间隔区(两条虚线)以外

为了解(3)式,需要用到拉格朗日乘子法(Method of lagrange multiplier),它是用来求解在约束条件目标函数的极值的,详细直观详解

注:以下解算过程希望完全看懂强烈建议理解阅读详细直观详解,很多地方推导过程只写必要过程及结论

根据约束的形式,我们引入m个拉格朗日乗法子,记为 $\boldsymbol \lambda = (\lambda_1,\ldots,\lambda_m)^T$ ,原因是,有m个约束,所以需要m个拉格朗日乗法子。可以得出拉格朗日方程如下:
$$\mathcal{L}(\mathbf w,b,\boldsymbol \lambda) = \frac{1}{2}\lVert \mathbf w \lVert ^2 - \sum_{i=1}^m \lambda_i \{ y_i(\mathbf w^T\vec x_i+b) -1 \} \tag{4}$$

解这个拉格朗日方程,对 $\mathbf w$ 和 $b$ 求偏导数,可以得到以下两个条件
$$\mathbf w = \sum_{i=1}^m \lambda_i y_i \vec x_i \\0 = \sum_{i=1}^m \lambda_i y_i$$

将这两个条件带回公式(4),可以得到对偶形式(dual representaiton),我们的目的也变为最大化 $\mathcal{L}(\boldsymbol \lambda)$,表达式如下
$$arg \operatorname*{max}_{\boldsymbol \lambda}\mathcal{L}(\boldsymbol \lambda) = \sum_{i=1}^m \lambda_i - \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^m \lambda_i \lambda_j \vec x_i \vec x_j \mathbf x_i^T \mathbf x_j \\s.t. \qquad \lambda_i \geqslant 0, \forall i\;;\quad \sum_{i=1}^m \lambda_i y_i = 0 \tag{5}$$

以上表达式可以通过二次规划算法解出 $\boldsymbol \lambda$ 后,带回,求出$\mathbf w$ 和 $b$,即可得到模型
$$f(\mathbf x) = \mathbf w^T\mathbf x + b = \sum_{i=1}^m \lambda_i y_i \mathbf x_i^T \mathbf x + b \tag{6}$$

补充一些关于二次规划算法的相关,(3)式的约束是一个不等式约束,所以我们可以使用KKT条件得到三个条件:
$$\lambda_i \geqslant0 ;\quad y_i f(\mathbf x_i)-1 \geqslant0; \quad \lambda_i\{ y_i f(\mathbf x_i)-1 \}=0$$

使用这些条件,可以构建高效算法来解这个方程,比如SMO(Sequential Minimal Optimization)就是其中一个比较著名的。至于SMO是如何做的,考虑到现代很多SVM的Pakage都是直接拿来用,秉承着前人付出了努力造了轮子就不重复造的核心精神,直接调用就好

软间隔

已经说明了如何求得方程,以上的推导形式都是建立在样本数据线性可分的基础上,如果样本数据你中有我我中有你(线性不可分),应该如何处理呢?这里就需要引入软间隔(Soft Margin),意味着,允许支持向量机在一定程度上出错

SoftMargin

由上一节我们得知,约束为: $y_i(\mathbf w^T\vec x_i+b) \geqslant1,\;i = 1,2,\ldots,m$ ,目标是使目标函数可以在一定程度不满足这个约束条件,我们引入常数 $C$ 和 损失函数 $\ell_{0/1}(z)$ 为0/1损失函数,当z小于0函数值为1,否则函数值为0
$$\operatorname*{min}_{\mathbf w,b} \frac{1}{2}\lVert w \lVert^2 + C \sum_{i=1}^m \ell_{0/1}(y_i(\mathbf w^T\vec x_i+b) -1) \tag {7}$$

对于(7)式来说 $C \geqslant 0$ 是个常数,当C无穷大时,迫使所有样本均满足约束;当C取有限值时,允许一些样本不满足约束

但 $\ell_{0/1}(z)$ 损失函数非凸、非连续,数学性质不好,不易直接求解,我们用其他一些函数来代替它,叫做替代损失函数(surrogate loss)
$$\begin{align}& \text{hinge损失:} \ell_{hinge}(z) = max(0,1-z)\\& \text{指数损失:} \ell_{exp}(z) = e^{-z}\\& \text{对数损失:} \ell_{log}(z) = log(1+e^{-z})\\\end{align}$$
三种常见损失函数如下图

损失函数

为了书写方便,我们引入松弛变量(slack variables): $\xi_i \geqslant 0$,可将(7)式重写为
$$\operatorname*{min}_{\mathbf w,b,\xi_i} \frac{1}{2}\lVert w \lVert^2 + C \sum_{i=1}^m \xi_i \\ s.t. \quad y_i(\mathbf w^T\vec x_i+b) \geqslant 1 - \xi_i ;\; \xi_i \geqslant 0,\; i = 1,2,\ldots,m \tag{8}$$
(8)式就是常见的软间隔支持向量机,其中,每一个样本都有一个对应的松弛变量,用以表征该样本不满足约束的程度,求解的方法同理硬间隔支持向量机

支持向量机扩展

核方法

以上我们求解的支持向量机都是在线性情况下的,那么非线性情况下如何处理?这里就引入:核方法

对于这样的问题,可以将样本从原始空间映射到一个更高为的特征空间,使得样本在这个特征空间内线性可分直观可视化解释

为了完成这个目的,令 $\phi(\mathbf x)$ 表示将 $\mathbf x$ 映射后的特征向量,于是,在特征空间划分超平面所对应的模型可表示为:
$$f(\mathbf x) = \mathbf w^T \phi(\mathbf x) + b$$

同理上文中引入拉格朗日乘子,求解整个方程后可得
$$\begin{align}f(\mathbf x) &= \mathbf w^T \phi(\mathbf x) + b \\&= \sum_{i=1}^m \lambda_i y_i \phi(\mathbf x_i)^T \phi(\mathbf x) + b \\&= \sum_{i=1}^m \lambda_i y_i k(\mathbf x,\mathbf x_i)+ b\end{align}$$

这里的函数 $k(\cdot,\cdot)$ 就是核函数(kernel function),常见的核函数见下表

名称 表达式 参数
线性核 $\boldsymbol x_i^T \boldsymbol x_j$
多项式核 $(\boldsymbol x_i^T \boldsymbol x_j)^d$ $d \geqslant 1$ 多项式次数
高斯核 $exp(-\frac{\lVert\boldsymbol x_i - \boldsymbol x_j \lVert^2}{2\sigma^2})$ $\sigma>0$ 高斯核带宽
拉普拉斯核 $exp(-\frac{\lVert\boldsymbol x_i - \boldsymbol x_j \lVert^2}{\sigma})$ $\sigma>0$
Sigmoid核 $tanh(\beta \boldsymbol x_i^T\boldsymbol x_j + \theta)$ $\beta>0$ $\theta>0$

也可以通过函数组合得到这些值

多类问题

多类问题可以使用两两做支持向量机,再由所有的支持向量机投票选出这个类别的归属,被称为one-versus-one approace

Reference
知乎各类回答
Wiki百科
PRML
周志华-机器学习

🔲 ⭐

Dota2伤害类型详解

【阅读时间】5min 百科类
【内容简介】有关Dota2所有伤害来源的总结和互相作用总结,方便查阅

伤害来源

攻击伤害

主要来自于普通物理攻击 - 平A

具体计算方式

技能伤害

详情参见

包含了所有来自技能的伤害,三种类型:魔法,物理和纯粹

伤害类型

互相作用机制表格

游戏机制 物理攻击 物理技能 魔法伤害 纯粹伤害
护甲 降低 降低 正常 正常
伤害格挡 降低 正常^1 正常 正常
魔法抗性 正常 正常 降低 正常
虚无 没有效果 美国效果 降低 正常
闪避 可能落空 正常 正常 降低
致盲 可能落空 正常 正常 正常
伤害加深^2 加深 加深 加深 加深
伤害减免^2 降低 降低 降低 降低
伤害无效化^2 没有效果 没有效果 没有效果 没有效果
魔法伤害护盾 正常 正常 没有效果 正常
无敌 没有效果 没有效果 没有效果 没有效果
技能免疫 正常 不定 不定 不定

物理

与护甲和伤害格挡有关,虚无和一些技能可以造成物理免疫,召唤单位和守卫的平A也是物理攻击

物理免疫技能

炼金术士:酸性喷雾 炼金术士:不稳定化合物 敌法师:法力损毁 兽王:野性飞斧 赏金猎人:暗影步 钢背兽:针刺扫射 人马:反击 克林克次:灼热之箭 戴泽:剧毒之触 戴泽:暗影波 死亡先知:驱使恶灵 龙骑士:古龙形态溅射 上古巨神:裂地沟壑 上古巨神:回音重踏 灰烬之灵:无影拳 主宰:无敌斩 昆卡:潮汐使者 拉席克:恶魔赦令 噬魂鬼:盛宴 露娜:月刃 马格纳斯:加强力量溅射 司夜刺客:复仇 剃刀:风暴之眼 力丸:背刺 斯拉达:鱼人碎击 斯拉达:重击 狙击手:爆头 熊灵:缠绕之爪 斯温:巨力挥舞 圣堂刺客:隐匿 潮汐猎人:锚机 熊战士:怒意狂击 冥界亚龙:幽冥剧毒 编织者:虫群

魔法

大多数技能都是魔法伤害,虚无状态会承受更多伤害

在纯粹和物理技能中未提及的都是魔法伤害

纯粹

纯粹伤害能作用与技能免疫单位,不能作用于无敌单位

斧王-反击螺旋 祸乱之源:蚀脑 祸乱之源:噩梦 嗜血狂魔:血之祭祀 嗜血狂魔:割裂 陈:忠诚考验 末日使者:末日 魅惑魔女:推进 谜团:午夜凋零 谜团:黑洞 祈求者:炎阳冲击 杰奇洛:A烈焰焚身 莉娜:A神灭斩 美杜莎:(石化)秘术异蛇 司夜刺客:尖刺外壳 全能骑士:洗礼 殁境神蚀者:奥术天球 帕吉:肉钩 痛苦女王:超声冲击波 沉默术士:智慧之刃 幽鬼:荒芜 圣堂刺客:灵能之刃 伐木机:锯齿飞轮 伐木机:伐木锯链 伐木机:死亡旋风 修补匠:激光 骨灰

标记

是一种特殊标记,为的是与其他技能区分开来

生命移除标记

某些生命移除标记的技能可以立即杀死幻想

干扰者:恶念瞥视 莱恩:妖术 莱恩:法力抽取 美杜莎:石化凝视 帕格纳:生命吸取 羊刀:变羊 暗影萨满:变羊

有些技能利用生命移除来制造生命消耗效果,通常都是非致命伤害伤害类型也是纯粹,被标记为生命移除

臂章:扣血 哈斯卡:沸血之矛对自身 艾欧:过载 凤凰:凤凰冲击 凤凰:烈火精灵 凤凰:烈日炙烤 魂戒:献身 工程师:自爆起飞 不朽尸王:噬魂

不反弹标记

不反弹标记会使得一些受到伤害事件不会与带有不反弹标记的伤害相互作用,这防止了无限伤害循环(

刃甲:反弹伤害 司夜刺客:尖刺外壳 冥界亚龙:腐蚀皮肤 术士:致命链接

🔲 ☆

Dota2机制总结

【阅读时间】百科类型文章
【内容简介】这是一份有关Dota2游戏机制的总结,核心目的是为了方便查阅,计算公式。针对人群是对数据和游戏机制有很大兴趣的高玩,从中你可能能了解如何通过击杀或得更多的经济哪些操作可以躲避技能等等

版本信息:更新到7.09

金钱

击杀英雄奖励

获得金钱 = 110 + 连杀奖励 + (被击杀者等级 * 8)

连杀奖励 = 60 * (连杀数-2)[大于0]

助攻奖励

助攻英雄数 获得金钱
1 财产总和贫穷系数 × 财产总和排名系数 × ( 126 + 4.5 × 阵亡英雄等级 + 财产总和前期系数 × 90 + 财产总和系数 × 0.03375 )
2 财产总和贫穷系数 × 财产总和排名系数 × ( 63 + 3.6 × 阵亡英雄等级 + 财产总和前期系数 × 67.5 + 财产总和系数 × 0.03375 )
3 财产总和贫穷系数 × 财产总和排名系数 × ( 31.5 + 2.7 × 阵亡英雄等级 + 财产总和前期系数 × 45 + 财产总和系数 * 0.03375 )
4 财产总和贫穷系数 × 财产总和排名系数 × ( 22.5 + 1.8 × 阵亡英雄等级 + 财产总和前期系数 × 31.5 + 财产总和系数 × 0.027 )
5 财产总和贫穷系数 × 财产总和排名系数 × ( 18 + 0.9 × 阵亡英雄等级 + 财产总和前期系数 × 22.5 + 财产总和系数 × 0.02025 )
  • 7.11更新:
    • 【新】(阵亡英雄财产综合 × 0.026 + 70) / 击杀涉及英雄的数量
    • 【旧】财产总和前期系数 × {} + 财产总和系数 × {}

财产总和差异 = ( 敌方总经济/己方总经济 ) - 1 【最大值1,最小值0】

【财产总和系数】 = 财产总和差异 * 阵亡英雄财产总和

【财产总和前期系数】 = (敌方总经济 - 己方总经济)/4000 【最大值为1】

【财产总和贫穷系数】 = 1.3 - 0.1 * 财产总和排名(阵亡英雄经济在队伍中的排名)

【财产总和排名系数】 1/2/3/4/5个英雄中,最富的到最穷的分别为{1} / {0.7; 1.3} / {0.7; 1; 1.3} / {0.7; 0.7; 1.3; 1.3} / {0.7; 0.7; 1; 1.3; 1.3}

Roshan

200 团队奖励

150 - 400 击杀奖励

死亡掉钱

损失不可靠金钱 = 50 + 财产总和 ÷ 40

5千经济 ➜ 175 1万经济 ➜ 290 2万经济 ➜ 550

复活

复活时间

  • 每级增加2秒
  • 每到6级的倍数增加10秒
  • 18级后每级增加4秒

买活花费

买活金钱 = 100 + (英雄等级 英雄等级 1.5) + (游戏时间(s) * 0.25 )

放弃比赛

掉线超过5分钟后,所有金钱被队友平分

物理攻击伤害

最终攻击伤害

$$最终攻击伤害 = \\ \{ [\text {MD} × (1 + \sum \text {PBD}) + \text {FBD}] \times \text {CSM} - \text {BD} \}\\ \times \text {AVM} \times \text {ATM} \times \text {GDM}$$

wiki链接

MD(Main Damage) 主要攻击力【白字攻击力】

主要攻击力 = 基础攻击力 + 主属性

除此之外,所有加成的攻击力都是【绿字攻击力】

PBD(Percentage bonus damage) 百分比攻击力加成

这个加成是加法叠加

Tips: 圣者遗物可以增加60攻击力,一个出支配带头狼的VS,36%+30% = 66%,60 / 0.66 = 90,也就是说,白字攻击力达到90就等于这个英雄出了一个圣者遗物,相当的可怕。

技能 加成数值
头狼光环 30%
强化图腾 100%/200%/300%/400%
野性驱使 15%/26%/37%/48%(只影响狼人控制的单位)
授予力量 20%/30%/40%/50% (天赋 30%/40%/50%/60%)
神之力量 80%/120%/160% (友军A帐: 75%/100%/125%)
复仇光环 12%/20%/28%/36% (天赋 32%/40%/48%/56%)
祭品光环 15%

FBD(Flat Bonus Damage) 固定值百分比加成

技能 加成数值 其他数值
臂章-邪恶之力 31
嗜血渴望 最高攻击力加成/英雄: 16/24/32/40 按照敌方英雄生命值百分比线性变化
战意 最高叠加层数: 5/7/9 每层攻击力加成: 18/24/30 持续时间:14s
极度饥渴 60/100/140 持续时间:14s
死亡契约 基于目标最大生命值的攻击力加成: 5%/7%/9% 持续时间:65s
精准光环 敏捷 20%/26%/32%/38% (天赋26%/32%/38%/44%) 范围:全地图
星体游魂 小兵6/9/12/15 英雄 12/24/36/48 (天赋92/104/116/128) 持续时间:9s
灵动迅捷 10/25/40/55/70/85/100 A帐115 持续时间:9s
卡尔-火 4/8/12/16/20/24/28 * 3
决斗 10/14/18 (天赋50/54/58) 永久存在buff
战斗嚎叫 70/100/130 持续时间:6s
月之祝福 14/22/30/38 光环范围:900
狼人 - 嚎叫 英雄 10/15/20/25 非英雄 4/6/8/10 夜晚翻倍 持续时间:13s
静电连接 每秒偷取 7/14/21/28 (天赋21/28/35/42) 链接时间:8s 持续时间:18s
支配死灵 最大灵魂数: 18/24/30/36 (A帐 22/30/38/46) 每个灵魂攻击力:2(天赋4)
折光 攻击次数: 3/4/5/6 (天赋6/7/8/9)攻击力加成: 20/40/60/80 持续时间:17s
魔化 20/40/60/80 持续时间:40/44/48/52s
长大 30/45/60 加主要攻击力
衰退光环 英雄死亡 30/35/40/45 小兵死亡 5 持续时间:30/40/50/60s

CSM(Critical Strike Multiplier) 致命一击倍数

致命一击来源 几率% 伤害% DPS期望%
头狼 - 致命一击 20 200 20
血棘 - 致命一击 20 175 15
酒仙 - 醉拳 10/15/20/25 230 13/19.5/26/32.5
混沌一击 12 125/175/225/275 3/9/15/21
水晶剑 - 致命一击 20 175 15
大炮 - 致命一击 30 235 40.5
剑舞 20/25/30/35 200% 20/25/30/35
狼人 - 变身 40 160/180/200 24/32/40
恩赐解脱 15 230/340/450 19.5/36/52.5
殊死一搏 15 英雄 150/200/250/300 7.5/15/22.5/30
血棘 - 灵魂撕裂 100 140 140
忍术 100 150/175/200/225 12/10/8/6 天赋 -5
棒击大地 100 150/175/200/225 天赋+100 触发条件
暗杀 100 A帐 280 距离内所有敌人
海象神拳 100 350/A帐500 冷却 36/24/12

游戏中出现的红字代表的是减少前的物理伤害

BD(Blocked Damage) 被格挡伤害

伤害格挡来源 几率% 格挡伤害
圆盾 50 近战16 远程8
穷鬼盾 英雄100 非英雄50 近战20 远程10
先锋盾 50 近战70 远程35
赤红甲 - 坚盾 100 60
海妖外壳 100 12/24/36/48

伤害格挡不格挡物理伤害技能,守卫的攻击也不格挡

AVM(Armor Value Multiplier) 护甲值倍数

护甲

ATM(Armor Type Multiplier) 护甲类型倍数

攻击类型,英雄打英雄100%

GDM(General Damage Multipliers) 一般伤害倍数

伤害调整

攻击类型

基础打英雄护甲75%伤害

穿刺打英雄护甲50%伤害,基础护甲(小兵护甲)150%伤害

伤害调整

伤害减免和加深

除了回光返照,幽灵船,魔法护盾之外,伤害减免和加深的叠加为加法叠加

技能 来源 数值%
血之狂暴 加深接受输出 25/30/35/40 远处 减半
赎罪 加深接受 18/24/30/36
灵魂猎手 加深接受 20/30/40/50
守卫冲刺 加深接受 15
血肉傀儡 加深接受 20/25/30 200范围内最高
回光返照 减免接受 0 全免 4/5/6 A帐+1
刚毛后背 减免接受 背后 16/24/32/40 侧面 减半
奔袭冲撞 减免接受 A帐 40 持续4秒
过载 减免接受 5/10/15/20
幽灵船 减免接受 40/45/50 持续10秒
决斗 减免接受 A帐 100 持续6/7/8秒
魔法护盾 减免接受 60 1.6/1.9/2.2/2.5
钻地 减免接受 40
折射 减免接受 10/14/18/22
激怒 减免接受 80 持续4秒
陵卫斗篷 减免接受 4层 8/12/16/20 冷却6/5/4/3
寒冬诅咒 减免接受 100 3.25/4/4.75
战斗饥渴 降低输出 A帐 30 持续10秒
白银之锋 降低输出 50 持续5秒

伤害无效化

伤害实例仍然存在,如果一些与伤害触发相关的事件并且没有低于伤害阈值的伤害,仍然会触发伤害事件

技能或物品名称 说明
无天光盾 110/140/170/200 天赋 +300 15s持续时间
回光返照 3/4/5(A 5/6/7) 伤害转化为治疗
凝魂之类 5次 大于50点的伤害抵挡120点
尖刺外壳 2.25s持续时间 无效化每个玩家的第一次伤害
守护天使 6/7/8 (A 8/9/10) 物理伤害无效化
虚妄芝诺 7/8/9(天赋 +2) 持续时间结束受到被无效化的伤害
折光 次数 3/4/5/6(天赋 +3) 忽略低于5点的伤害
活体护甲 所有类型伤害无效化 20/40/60/80 次数 4/5/6/7(天赋 +4) 持续15s 低于5伤害忽略
极寒之拥 持续时间4s 无效物理伤害

技能攻击伤害

技能伤害计算

魔法伤害受到魔法抗性影响,技能伤害可以由智力获得增强

$$增强数值 = [初始智力 + (当前等级 - 1) \times 智力成长] / 14 / 100 + 技能增强天赋$$$$技能最终伤害 = 技能伤害数值 \times (1 + 增强数值) \times \\ \prod_{i=1}^n{(1 - 魔法抗性增加_i)} \times\prod_{i=1}^n{(1 + 魔法抗性降低_i)}$$

技能增强天赋

远古冰魄10:8% 蝙蝠骑士15:5% 人马20:10% 死亡先知10:5% 干扰者20:10% 大地之灵20:15% 灰烬之灵10:8% 矮人直升机10:6% 杰奇洛10:8% 拉西克20:5% 莉娜20:6% 莱恩20:8% 马格纳斯10:15% 米拉娜15:5% 食人魔魔法师25:15% 殁境神蚀者25:8% 凤凰20:8% 帕克20:10% 拉比克20:8% 暗影恶魔15:8% 影魔15:6% 风暴之灵25:10% 伐木机20:5% 修补匠15:4% 孽主15:12% 维萨吉25:20% 风行者20:15%

技能伤害类型

分为:魔法伤害,物理伤害,纯粹伤害

大部分伤害为魔法伤害

物理伤害技能

炼金术士:酸性喷雾 炼金术士:不稳定化合物 敌法师:法力损毁 斧王:反击螺旋 兽王:野性飞斧 赏金猎人:暗影步 钢背兽:针刺扫射 人马:反击 克林克兹:灼热之箭 戴泽:剧毒之触 戴泽:暗影波 死亡先知:驱使恶灵 主宰:无敌斩 昆卡:潮汐使者 拉西克:恶魔的赦令 噬魂鬼:盛宴 剃刀:风暴之眼 斯拉达:鱼人碎击 斯拉达:深海重击 狙击手:爆头 工程师:感应地雷 工程师:爆破起飞 潮汐猎人:锚机 熊战士:怒意狂击 冥界亚龙:幽冥剧毒 编织者:虫群

纯粹伤害技能

祸乱之源:蚀脑 祸乱之源:噩梦 刃甲:反弹伤害 嗜血狂魔:血之祭祀 嗜血狂魔:割裂 陈:忠诚考验 死亡先知:吸魂巫术 末日使者:末日 魅惑魔女:推进 谜团:午夜凋零 祈求者:电磁脉冲 祈求者:阳炎冲击 莉娜:神灭斩A帐 美杜莎:石化后秘术异蛇 司夜刺客:尖刺外壳 全能骑士:洗礼 殁境神蚀者:奥术天球 帕吉:肉狗 痛苦女王:超声波冲击 沉默术士:智慧之刃 幽鬼:荒芜 圣堂刺客:灵能之刃 伐木机:锯齿飞轮 伐木机:伐木锯链 伐木机:带树木死亡旋风 修补匠:激光 骨灰

攻击速度

基础攻击间隔 BAT

英雄在没有额外攻速加成的情况下每两次攻击间的时间间隔

攻击速度 ISA

  • 面板中英雄增加的攻击速度
  • 由装备获得的攻击速度加成
  • 每个英雄基础100点基础攻速
  • 由Debuff造成的攻速减低

攻击速度计算公式

$$每秒攻击的次数 = \frac{(100 + IAS) × 0.01} {BAT}$$$$每次攻击的时间 = \frac{1}{每秒攻击的次数}$$
攻击速度 效果
-80 五分之一BAT时间来攻击
-75 四分之一BAT时间来攻击
-66 三分之一BAT时间来攻击
-50 二分之一BAT时间来攻击
+00 正常状态
+100 * n (1+n)倍攻击速度

根据表格我们可以知道减攻速的技能在基础攻速很高的情况下基本没有什么效果,但是越接近0速度,减速效果越明显

增加攻击速度技能列表

技能 增加数值 持续时间s
魔霭诅咒 10/20/30/40 4.5
雷肤兽 - 暴怒 75 8
雷肤兽 - 战鼓光环 15 光环范围 900
天穹守望者 - 磁场 50/60/70/80 3.5/4.5/5.5/6.5
淘汰之刃 30 6 A帐10 成功淘汰
野性之心 15/25/35/45 光环范围 900
扫射 130 天赋 +70 4/6/8/10
熊怪 - 迅捷光环 15 光环范围 900
飓风之力 100 5
狂战士之血 220/260/300/340 剩下10%生命值最高
灵动迅捷 10/25/40/55/70/85/100/A115 9
卡尔 - 雷 2/4/6/8/10/12/14 * 3 开关
过载 40/50/60/70 开关
强攻 65/90/115/140 5
狂暴 50/60/70/80 3/4/5/6
炽魂 每层40/55/70/85(天赋 75/90/105/120) 10 最高3层
德鲁伊 - 狂猛 10/20/30/40 18/22/26/30
跳跃 16/32/48/64 (天赋 +100) 5
死灵射手光环 5/7/9 光环范围 900
暗夜猎影 45/60/75/90 夜晚
嗜血术 30/40/50/60 (天赋 +40) 30
幻影突袭 130 4s or 4次攻击
战斗专注 60/120/180 5
热血战魂 15/20/25/30 (105/140/175/210) 每次攻击同个目标
超强力量 400 15 or 3/4/5/6次攻击
黄泉颤抖 64 3/4/5/6
集中火力 500 20
寒冬诅咒 70 3.25/4/4.75

降低攻击速度技能

比较有效果的降低攻速的技能

烈火精灵:80/100/120/140 不可侵犯:40/70/100/130蝮蛇突袭:40/60/80 重生:75 黄泉颤抖:64 小狼-致残:60 冰封魔印:30/40/50/60 雷霆一击:25/35/45/55 原始咆哮:50 冰霜新星:20/30/40/50 液态火:20/30/40/50 石化凝视:50 夜魔虚空:50 冰眼:45 豪猪:10/20/30/40 冰火交加:28/32/36/40 毒龙法球:10/20/30/40 全能光环:10/18/26/34

护甲

白字护甲

$$
敏捷 = 基础敏捷 + (等级 - 1) * 敏捷成长
$$

$$
白字护甲 = 基础护甲 + ( \frac{敏捷}{7})
$$

护甲值倍数

$$
护甲值倍数 = 1 - \frac{0.06 \times 护甲值}{1 + 0.06 \times |护甲值| }
$$

护甲值倍数倍数和护甲值的相关曲线

相关曲线
相关曲线

纵坐标是护甲值倍数,横坐标是现在英雄的护甲,不同颜色的线是此时减少的护甲(越上面的线减的越多)

有效生命值 (EHP)

有效生命值 = 总生命值 ÷ 护甲值倍数

$$实时有效物理生命值 = 当前生命值 \div (1 - \frac{0.06 \times 当前总护甲值}{1 + 0.06 \times |当前总护甲值| })$$$$实时有效魔法生命值 = 当前生命值 \div (0.75 \times (1 - 装备提供抗性_1) \times \ldots \times (1 - 装备提供抗性_n))$$

护甲调整

增加护甲的技能

技能 加成数值 持续时间s
黑龙 - 龙肤光环 3 光环范围 900
狂战士怒吼 40 2/2.4/2.8/3.2
编织 0.75/1.0/1.25 每秒 18/24/30 24
龙族血统 3/6/9/12(天赋 翻倍) 永久
霜冻护甲 3/5/7/9 40
战斗嚎叫 10/15/20 6
变形术 4/6/8 变形状态
寒冰盔甲 8 45
战吼 5/10/15/20 8
活性护甲 5/10/15/20 每层 1/1.2/1.4/1.6 10/13/16/19
崎岖外表 3/4/5/6 永久
巨魔 - 狂战士之怒 6 切换

减低护甲的技能

技能 降低数值 持续时间s
酸性喷雾 4/5/6/7 (天赋 +4) 16
远古 - 亵渎 50% 6
粘稠鼻液 1/1.4/1.8/2.2 最高层数4(8) 英雄5 小兵10
实相裂隙 3/4/5/6 8
编织 0.75/1/1.25每秒 (18/24/30) 24
自然秩序 基础护甲:40%/60%/80%/100% 光环范围 275
火人 - 攻击 每次1点 上限10 5 击中刷新时间
激流 2/3/4/5 8 范围 320
风暴之眼 0.7/0.6/0.5 (天赋 -0.1) 打击1次1点 30
魔王降临 3/4/5/6 光环范围 900
侵蚀雾霭 10/15/20 18
隐匿 2/4/6/8 10
巨浪 3/4/5/6 (天赋 +5) 4
死亡旋风 敏捷损失 * 0.14 14
恐怖波动 3/4/5/6 1400距离 300范围 15
虫群 1.4/1.25/1.1/0.95 攻击一次1点 16

护甲相关装备

强袭 +5 玄冥盾牌系列 +2 勋章 +7 天鹰 +2 炎阳纹章 +10 祭品 +4

黯灭 -7 勋章 -7 炎阳纹章 -10 强袭 -5 枯萎之石 -2 疯脸 -5

闪避

机制

闪避与致盲都会在攻击完成(弹道击中)时有一定几率触发

叠加与计算

多个闪避来源乘法叠加

上下坡落空几率

如果攻击者处于比目标更低的位置时,远程攻击会有25%的几率落空。

攻击者和目标之间的地形的高低差异实在击中目标时决定的,中路对线过程中,可以使用弹道飞行过程位移来保持和目标的同样地形高度保证必中

飞行单位无上下坡落空几率

计算公式

$\prod_{i=0}^n$ 的含义是把i=0到n所有的项相乘
$$落空几率 = \prod_{i=0}^n (1 - 闪避来源_i) \times \prod_{j=0}^n (1 - 致盲来源_j) \times 上下坡落空几率$$$$命中几率 = 1 - \prod_{i = 0}^n{(1 - 必中/克敌先机来源_i)}$$$$最终命中几率 = 1 - 落空几率 \times (1 - 命中几率)$$$$最终落空几率 = 落空几率 \times (1 - 命中几率)$$
公式只是为了程序数值计算使用,是需要记住:每一次攻击要绕过所有的闪避成功命中,只有当所有的闪避都失败了,这次攻击才可以造成伤害。所以说,出很多个闪避装备,在一定程度上对物理核心非常克制,这时候物理核心必须出金箍棒

闪避来源

技能或物品名称 闪避几率%
敌法师 - 20级右天赋 15
磁场 100 3.5/4.5/5.5/6.5s
醉拳 10/15/20/25 一段时间的100%闪避
赏金猎人 - 25级右天赋 25
蝴蝶 35
人马 - 15级右天赋 10
克林克兹 - 20级右天赋 20
虚空 - 25级右天赋 20
黑暗贤者 - 10级右天赋 12
天堂之戟 25
噬魂鬼 - 20级右天赋 15
狼人 - 20级右天赋 15
美杜莎 - 15级左天赋 15
米波 - 20级右天赋 10
大圣 - 10级左天赋 12
模糊 20/30/40/50
猴子 - 20级左天赋 15
炎阳纹章 20
炎阳纹章- 队友使用 - 日耀 20 7s
斯温 - 20级左天赋 20
闪避护肤 20
圣堂刺客 - 15级左天赋 12
风行 100 3/4/5/6a

致盲来源

技能或物品名称 落空几率%
醉酒云雾 70 4s
麻痹之咬 30/40/50/60 2s
致盲之光 80 3/4/5s
伤残恐惧 白天10 3s 夜晚50 5/6/7/8s
辉耀 - 辉耀灼烧 17
烟雾 40/50/60/70 6s
激光 100 3/3.5/4/4.5s 小兵 6s
近战旋风飞斧 60 4/5/6/7s

克敌机先来源

为一种攻击特效,防止该次攻击落空,用来反制闪避,致盲,以及远程单位上下坡的25%几率落空,也能够防止近战攻击由于目标在攻击之前超过了350距离而落空

但是攻击弹道依旧可以躲避

对建筑物无效

技能或物品名称 备注 不会落空为100%
强化图腾 带有Buff的一次攻击不会落空
棒击大地 不会落空的即时攻击
金箍棒 每次攻击带有克敌先机
复仇 破影一击不会落空
窒息之刃 不会落空的即时攻击
白银之锋 - 暗影步 破影一击不会落空
暗杀 需要A帐
自然庇护 破影一击不会落空
海象神拳! 不会落空
死亡守卫 需要A帐 不会落空

必中来源

必中防止一个单位受到的任何攻击落空

血棘的灵魂撕裂,岗哨守卫,炎阳纹章给敌方使用提供35%的必中效果

移动速度

英雄移动速度表

叠加

相似的装备提供的移动速度不叠加,除了风帐

多个鞋类物品不叠加

夜叉 散夜对剑 幻影斧不叠加

多个战鼓或风灵之纹不叠加

风灵之纹和战鼓鞋类物品叠加

公式

移动速度 = (基础移动速度 + 具体移动速度加成) * (1 + 百分比移动速度加成和减速的和)

转身速度

转身速率表

英雄 基础转身速率 转180°时间
凤凰, 噬魂鬼, 影魔, 石鳞剑士, 虚空假面, 蝙蝠骑士, 钢背兽 1 0.094
撼地者 0.9 0.105
风暴之灵, 风行者, 马格纳斯 0.8 0.118
卓尔游侠, 圣堂刺客, 巨牙海民, 帕吉, 拉比克, 狙击手, 艾欧, 邪影芳灵 0.7 0.135
米波 0.65 0.145
不朽尸王, 主宰, 伐木机, 修补匠, 先知, 全能骑士, 力丸, 发条技师, 变体精灵, 复仇之魂, 大地之灵, 天穹守望者, 孽主, 宙斯, 幻影刺客, 幻影长矛手, 戴泽, 斧王, 斯温, 昆卡, 暗影恶魔, 沉默术士, 炼金术士, 矮人直升机, 祸乱之源, 赏金猎人, 远古冰魄, 酒仙, 陈, 露娜, 食人魔魔法师, 黑暗贤者, 齐天大圣, 龙骑士 0.6 0.157
上古巨神, 亚巴顿, 光之守卫, 克林克兹, 兽王, 军团指挥官, 冥界亚龙, 冥魂大帝, 剃刀, 剧毒术士, 半人马战行者, 司夜刺客, 哈斯卡, 嗜血狂魔, 天怒法师, 娜迦海妖, 寒冬飞龙, 小小, 工程师, 巨魔战将, 巫医, 巫妖, 帕克, 帕格纳, 干扰者, 幽鬼, 德鲁伊, 恐怖利刃, 拉席克, 敌法师, 斯拉克, 斯拉达, 暗夜魔王, 暗影萨满, 末日使者, 术士, 杰奇洛, 树精卫士, 死亡先知, 殁境神蚀者, 水晶室女, 沙王, 混沌骑士, 潮汐猎人, 灰烬之灵, 熊战士, 狼人, 痛苦女王, 瘟疫法师, 祈求者, 神谕者, 米拉娜, 维萨吉, 编织者, 美杜莎, 育母蜘蛛, 莉娜, 莱恩, 裂魂人, 谜团, 魅惑魔女 0.5 0.188

大部分英雄的转身速度都比较慢,第一梯队1-0.7速率几个英雄在这方面有明显的优势

特殊说明

  • 艾欧和石鳞剑士,执行命令不需要转身,如果是技能需要转身,但使用物品不需要转身
  • 无敌斩 无影拳 凤凰冲击 烈日炙烤 期间,不需要转身执行
  • 使用 洪流 暗影护符 微光披风 魔瓶 净化药水 魔法芒果 治疗药膏 都不需要转身面向目标

影响转身速率的技能

技能名称 效果
蝙蝠骑士 - 粘性燃油(叠油) 转身速率减缓:70% 持续时间:8s
美杜莎 - 石化凝视 转身速率减缓:35% 持续时间:5/6/7
石鳞剑士 - 地雷滚滚(对自身) 转身速率:0.063
初始/跳跃/反弹后转身速率加成:0.086
转身速率加成持续时间:0.25
凤凰 - 烈日炙烤(对自身) 转身速率:0.013 = 每秒转25°(龟速)

魔法抗性

魔法抗性除了米波35%,维萨吉10%魔法抗性外,其他英雄都为25%基础魔法抗性

魔法抗性乘法叠加,不同的提高魔法抗性的装备可以叠加

魔法抗性加成来源

技能或物品名称 加成数值%及备注
法术护盾 26/34/42/50
小马or小熊怪光环 英雄5 非英雄20 可叠加
魔抗斗篷 15
微光披风 15 被动
微光披风 - 微光 45 5s 0.6s渐隐时间
挑战头巾 25
狂战士之血 20/30/40/50 最大10%生命值
洞察烟斗 30 被动
洞察烟斗光环 10
腐肉堆积 6/8/10/12
失效力场 10/14/18/22
腐蚀皮肤 10/15/20/25

魔法抗性减少来源

技能或物品名称 减少数值% 备注
冰霜漩涡 15/20/25/30 16s 0.5s粘滞时间
自然秩序 40/60/80/100 光环范围350 1s粘滞时间
虚化冲击 40 敌方3s 友方4s
幽灵形态 40 4s
幽魂护罩 20 3/3.5/4/4.5
衰老 30/40/50/60 3.5
上古封印 30/35/40/45 3/4/5/6
纷争面纱 25 16

魔法抗性100%来源

技能或物品名称 备注
黑皇杖 10/9/8/7/6/5
牺牲 跳跃时间or持续5s
剑刃风暴 5s
狂暴 3/4/5/6 (天赋+1s)
石化凝视 3s 天赋5s
驱逐 4/5/6/7
命运赦令 3/3.5/4/4.5

魔法吸收护盾

魔法吸收护盾计算是计算魔抗后的吸收数值,魔抗越高,护盾效果越好

任何类型魔法护盾无法叠加,同时吸收伤害

技能或物品名称 吸收数值
烈火罩 50/200/350/500 (天赋 +500)
挑战头巾 - 绝缘 325 持续12s
洞察烟斗 - 法术护盾 400 持续12s

施法距离

幕布笔记链接

物品被动效果叠加

独立叠加

  • 攻击力
  • 属性加成
  • 魔法值/生命值
  • 生命恢复速率/魔法恢复速率(基础速率 * 加成倍数)
  • 攻击速度加成
  • 护甲加成
  • 分裂区域
  • 移动速度加成

乘法叠加

出现边缘递减效应
$$
加成 = 1 - (1-x) \times (1-y) \times (1-z) \times \ldots
$$
其中 $x y z$ 都表示一个百分比

魔法抗性乘法叠加

  • 一个100点魔法伤害的技能
  • 英雄本身25%魔法抗性,伤害变为 100 * (1 - 25%) = 75
  • 再装备挑战头巾,再降低30%,伤害变为 75 * (1 - 30%) = 52.5

躲避

躲避是一种躲避弹道的行为,更确切的说,是使弹道完全失去跟踪目标能力的行为。白话文就是:秀操作,骚

躲避技能的方式

技能

以下技能在施法时能躲避弹道

炼金术士:化学狂暴 酒仙:元素分离 混沌骑士:混沌之军 噬魂鬼:感染`幻影斧:镜像变体精灵:波浪形态娜迦海妖:镜像幻影长矛手:神行百变凤凰:超新星帕克:相位转移力丸:绝杀秘技风暴之灵:球状闪电`

传送

所有的真闪烁都能躲避弹道,躲避发生在使用技能移动时

敌法师:闪烁 闪烁匕首:闪烁 远行鞋:传送 艾欧:传送(只有艾欧传送过去时可以躲避) 先知:传送 帕克:灵动之翼 痛苦女王:闪烁 熊灵:回归 回城卷轴:传送 孽主:黑暗之门 编织者:时光倒流 陈:忠诚考验 光之守卫:召回 变体精灵:替换复制品

隐身

所有能获得隐身状态的技能技能都能躲避弹道,除非敌人的在弹道到达之前使用了反隐,但是必须要注意不同技能的渐隐时间

隐藏

变为临时性的隐藏不能躲避弹道。

躲避与变为隐藏无关,而是与技能本身有关。这意味着隐藏技能不一定都能躲避弹道,

但是,利用合适的时机,可阻止弹道或一般技能,击中施法者或目标

隐藏来源有一下技能

酒仙:元素分离 混沌骑士:混沌之军 大地之灵:残炎魔咒 噬魂鬼:吸收 幻影斧:镜像 娜迦海妖:镜像 殁境神蚀者:星体禁锢 幻影长矛手:神行百变 凤凰:超新星 帕克:相位转移 力丸:绝杀秘技 暗影恶魔:崩裂禁锢 巨牙海民:雪球

无敌

变为无敌不能躲避弹道,但是可以在击中时减轻或使其效果无效。

攻击伤害和技能伤害会被忽略。有一些技能可以影响无敌单位

无敌来源

祸乱之源:噩梦 酒仙:元素分离 混沌骑士:混沌之军 大地之灵:残岩魔咒 灰烬之灵:无影拳 灰烬之灵:激活残焰 风帐:龙卷风 虚空假面:时间漫游 佣兽:石像形态 祈求者:强袭飓风 主宰:无敌斩 噬魂鬼:吸收 噬魂鬼:感染 幻影斧:镜像 变体精灵:波浪形态 娜迦海妖:镜像 娜迦海妖:海妖之歌 殁境神蚀者:星体禁锢 幻影长矛手:神行百变 凤凰:超新星 帕克:相位转移 力丸:绝杀秘技 暗影恶魔:崩裂禁锢 狂风:龙卷风 风暴之灵:球状闪电 巨牙海民:雪球

可以被躲避的弹道

任何单位和英雄的所有物理攻击的弹道都可以躲避

可以被躲避的技能

亚巴顿:迷雾缠绕 赏金猎人:投掷飞镖 酒仙:醉酒云雾 钢背兽:粘稠鼻涕 育母蜘蛛:孵化蜘蛛 混沌骑士:混乱之箭 陈:赎罪 戴泽:剧毒之触 龙骑士:神龙摆尾 大地:投掷巨石 撼地者:回音击 虚灵之刃:虚化冲击 变体精灵:变体攻击 泥土傀儡:投石 娜迦海妖:诱捕 食人魔魔法师:引燃 神谕者:气运之末 幻影刺客:窒息之刃 幻影长矛手:灵魂之矛 痛苦女王:暗影突袭 阿托斯:致残 天怒法师:震荡光弹 狙击手:暗杀 斯温:风暴之拳 潮汐猎人:巨浪 修补匠:导热飞弹 复仇之魂:魔法箭 冥界亚龙:蝮蛇突袭 维萨吉:灵魂超度 风行者:束缚击 寒冬飞龙:碎裂冲击 冥魂大帝:冥火暴击

不可以被躲避的技能

炼金术士:不稳定化合物 天穹守望者:闪光幽魂 爱人直升机:追踪导弹 哈斯卡:牺牲 拉西克:闪电风暴 巫妖:连环霜冻 莉娜:神灭斩 莱恩:死亡一指 美杜莎:秘术异蛇 米拉娜:流星风暴 瘟疫法师:死亡脉冲 痛苦女王:痛苦尖叫 拉比克:技能窃取 天怒法师:奥法鹰隼 幽鬼:幽鬼之刃 小小:投掷 树精卫士:寄生种子 巨牙海民:雪球 寒冬飞龙:碎裂冲击弹射 巫医:麻痹药剂

视野

Dota2中,掌握视野掌握主动权,通常来说,白天视野1800,夜晚视野800,装备银月之晶获得300额外夜间视野,吞噬获得150夜间视野

视野例外英雄

模型视野不同列表

英雄名称 白天 夜晚
斯拉克 1800 1800
暗夜魔王 800 1800
狙击手 1800 1100
蝙蝠骑士 1200 800
赏金猎人 1800 1000

增加视野技能

英雄名称 白天视野 夜间视野
露娜 - 月之祝福 1800 800/1050/1300/1550/1800
狼人 - 变身 1800 800/1800
寒冬飞龙 - 严寒灼烧 1800 800/1200

裂魂人【10级】天赋夜晚视野 +400

寒冬飞龙【15级】天赋夜晚视野 +500

斯拉达【20级】天赋夜晚视野 +1000

野怪也召唤单位视野

单位名称 白天 夜晚
尸王 - 不朽僵尸 1400 1400
丘陵巨魔牧师 1400 1400
死灵龙 - 佣兽(高空视野) 390 390
冥魂大帝 - 骷髅兵 800 600
术士 - 地狱火 1800 1800
酒仙 - 大地 1800 800
先知 - 大树人 500 500
兽王 - 战鹰 1000 1000
上古巨神 - 星体游魂(高空视野) 400 400
先知 - 树人 500 500
死灵射手/死灵战士 1300/1400/1500 800
酒仙 - 烈火 1800 800
德鲁伊 - 熊灵 1400 800
祈求者 - 熔炉精灵 1200 800
酒仙 - 狂风 1800 800
狗头人 1400 800
甲虫 321 321
狼人 - 精灵狼 1200 800
谜团 - 精神体 1200 800
蜘蛛 - 小蜘蛛 700 700
兽王 - 豪猪 1400 800
豺狼人刺客 400 400
远古岚肤兽 1400 800
远古雷肤兽 1400 800
鹰身女妖侦察者 1800 1800
鹰身女妖风暴巫师 1800 1800

视野类型

幕布笔记链接

❌