前言
非监督学习分为两大类:Clustering & Dimension Reduction 和Generation,这节主要是讲前者中的Dimension Reduction(Linear Methods),对PCA进行了重点讲解。
B站评论提示:非监督学习那里视频的顺序有错,应该是pca,ne,da(Deep Auto Encoder),we,dgm
公式输入请参考:在线Latex公式
概念
关于非监督学习(Unsupervised Learning)有两大类,如下图所示,前者是找一个function只有输入,然后得到输出(这个输出我们也不知道会长什么样子),后者是找一个function,输入一些数字(我们也不知道这个数字会对应什么图片),就会根据随机数字输出一些图片。
聚类(Clustering)
这个概念就不多说了,这里要注意聚类的类别数量,不能所有图片一个类,也不能每个图片一个类。常用方法:
K均值
这里注意:初始化的时候是随机从总体样本X中取K个样本,否则会出现某个样本不属于任何一个class的情况。
HAC
Hierarchical Agglomerative Clustering (HAC),
第一步:建树
例如有5个sample,然后算他们两两之间的similarity,上图左边两个和右边两个similarity最大,连起来。
然后继续算,变成上图,最后红和绿色连起来变成root。
第二步:分类(pick a threshold)
例如在图示蓝色位置切一刀,这五个样本就分成了三类,左边两个,中间一个,右边两个
同理,在红色位置切一下,就分成两类。
绿色位置可以分四类
这个方法的好处比起K均值而言,不需要决定分几个类,而只需要决定在哪里切(相对会比较简单)
类别的表示
如果非要把某个样本归为某一个类别实际上不太合理,老师举了一个二次元的例子(没看过。。。。。)图中的小杰并非属于某一个系别,而是有概率的。
老师的备注:克己派(Abnegation)、友好派(Amity)、無畏派(Dauntless)、直言派(Candor)以及博學派(Erudite)
因此要用Distributed Representation,用vector来表示,如果原来的对象是图片,比较高维,降维后表示就是Dimension Reduction
Dimension Reduction
上图就是一个3d的可以降维到2d的例子,下面看一个具体的例子。
In MNIST, a digit is 28 x 28 dims.
Most 28 x 28 dim vectors are not digits
在mnist中,数字虽然是用2828维的图片来表示,但是实际上用2828维向量表示起来就不是图片
其实不需要28*28维来表示数字,例如:
这里是用一维信息来表示数字3。这里只需要记录左转或右转多少度。
这里还接上上节课最后那里的一个梗:杨过VS胡子爷爷
Dimension Reduction怎么做
找一个function,它的input是一个vector x,它的output是另外一个vector z,由于是Dimension Reduction,所以
D
i
m
e
n
s
i
o
n
z
<
D
i
m
e
n
s
i
o
n
x
Dimension_z < Dimension_x
Dimensionz<Dimensionx
比较简单方法:Feature selection
如果数据的feature都是集中在某一个dimension上,如下图:
但是这样的情况很少,很多情况是这样:
上图中看上去很复杂,实际上是变量x经过了一个线性变换得到的形态,这个变化可以写为
z
=
W
x
z=Wx
z=Wx,找到W就能对其进行降维。
找W的方法就是PCA,这个很重要,另起一节讲。
Principle component analysis
这个方法就是说有这么一个关系
z
=
W
x
z=Wx
z=Wx,我们不知道
z
z
z的样子,就是要通过很多的
x
x
x找到
W
W
W,如何找到
W
W
W?先从一个降维到1d的例子看一看(Reduce to 1-D:):
z
1
=
w
1
⋅
x
z_1=w^1\cdot x
z1=w1⋅x
注意观察上式,
z
1
z_1
z1是一个scalar,
w
1
w^1
w1是
W
W
W的第一个row,
w
1
w^1
w1和
x
x
x点乘就是一个常数。
w
1
w^1
w1要长什么样?先假设
w
1
w^1
w1长度为1,也就是
w
1
w^1
w1的two norm为1:
∥
w
1
∥
2
=
1
\left \| w^1 \right \|_2=1
∥∥w1∥∥2=1,这样假设后意味着上式画出来是下面这个样子:
w
w
w和
x
x
x是高维空间中的向量,
z
1
z_1
z1是
x
x
x在
w
1
w^1
w1上的投影,如果有很多个
x
x
x,怎么来选
w
1
w^1
w1?例如,
x
x
x的分布如下(如果观察坐标轴,这是宝可梦乱入啊)
这里给了两个
w
1
w^1
w1的方向,咋选?目标应该是希望这么多的
x
x
x经过投影得到很多的
z
1
z_1
z1越大越好,也就是要找一个方向,使得投影后的
z
1
z_1
z1的variance越大越好。
这个
w
1
w^1
w1其实代表了宝可梦的强度factor,这个factor同时代表了宝可梦的防御和攻击力。
用数学来表示:
V
a
r
(
z
1
)
=
∑
z
1
(
z
1
−
z
1
ˉ
)
2
Var(z_1)=\sum_{z_1}(z_1-\bar{ z_1})^2
Var(z1)=z1∑(z1−z1ˉ)2
如果想要投影到二维就变成下面这个样子,就会有
z
2
z_2
z2、
w
2
w^2
w2,如果也和
w
1
w^1
w1一样让
z
1
z_1
z1的variance越大越好,那么求出来的
w
2
w^2
w2就和
w
1
w^1
w1一样了,所以,
这里有个额外的条件,就是上图中红色部分,就是
w
1
w^1
w1和
w
2
w^2
w2要垂直。
还可以继续往下扩展到n维,找出来的
w
1
w^1
w1、
w
2
w^2
w2。。。转置后堆起来就变成上图中的
W
W
W。这个玩意学名叫正交矩阵。每个行都是互相垂直的。如何解
w
1
w^1
w1、
w
2
w^2
w2。。。?
推导(Warning of Math)
先看基本条件:
z
1
=
w
1
⋅
x
z_1=w^1\cdot x
z1=w1⋅x
z
1
ˉ
=
1
N
∑
z
1
=
1
N
∑
w
1
⋅
x
=
w
1
⋅
1
N
∑
x
=
w
1
⋅
x
ˉ
\bar{z_1}=\cfrac{1}{N}\sum z_1=\cfrac{1}{N}\sum w^1\cdot x=w^1\cdot\cfrac{1}{N} \sum x=w^1\cdot\bar x
z1ˉ=N1∑z1=N1∑w1⋅x=w1⋅N1∑x=w1⋅xˉ
然后是要最大化的对象,并把上面的两个公式代入:
V
a
r
(
z
1
)
=
∑
z
1
(
z
1
−
z
1
ˉ
)
2
=
∑
x
(
w
1
⋅
x
−
w
1
⋅
x
ˉ
)
2
=
∑
(
w
1
⋅
(
x
−
x
ˉ
)
)
2
(1)
Var(z_1)=\sum_{z_1}(z_1-\bar{z_1})^2=\sum_x(w^1\cdot x-w^1\cdot\bar x)^2=\sum(w^1\cdot(x-\bar x))^2\tag1
Var(z1)=z1∑(z1−z1ˉ)2=x∑(w1⋅x−w1⋅xˉ)2=∑(w1⋅(x−xˉ))2(1)
这里根据下面的公式:
(
a
⋅
b
)
2
=
(
a
T
b
)
2
=
a
T
b
a
T
b
=
a
T
b
(
a
T
b
)
T
=
a
T
b
b
T
a
(a\cdot b)^2=(a^Tb)^2=a^Tba^Tb=a^Tb(a^Tb)^T=a^Tbb^Ta
(a⋅b)2=(aTb)2=aTbaTb=aTb(aTb)T=aTbbTa
这里
a
T
b
a^Tb
aTb是一个scalar,所以他转置后还是scalar,即
a
T
b
=
(
a
T
b
)
T
a^Tb=(a^Tb)^T
aTb=(aTb)T
把
a
=
w
1
a=w^1
a=w1和
b
=
(
x
−
x
ˉ
)
b=(x-\bar x)
b=(x−xˉ)带入
(
1
)
=
∑
(
w
1
)
T
(
x
−
x
ˉ
)
(
x
−
x
ˉ
)
T
w
1
(1)=\sum(w^1)^T(x-\bar x)(x-\bar x)^Tw^1
(1)=∑(w1)T(x−xˉ)(x−xˉ)Tw1
这里是对所有数据进行求和,所以和
w
1
w^1
w1无关,可以提到前面
=
(
w
1
)
T
∑
(
x
−
x
ˉ
)
(
x
−
x
ˉ
)
T
w
1
=(w^1)^T\sum(x-\bar x)(x-\bar x)^Tw^1
=(w1)T∑(x−xˉ)(x−xˉ)Tw1
这里求和是针对
∑
(
x
−
x
ˉ
)
(
x
−
x
ˉ
)
T
\sum(x-\bar x)(x-\bar x)^T
∑(x−xˉ)(x−xˉ)T,其他项在外面。
∑
(
x
−
x
ˉ
)
(
x
−
x
ˉ
)
T
\sum(x-\bar x)(x-\bar x)^T
∑(x−xˉ)(x−xˉ)T就是
x
x
x的covariance matrix。
因此:
(
w
1
)
T
C
o
v
(
x
)
w
1
,
其
中
可
以
记
S
=
C
o
v
(
x
)
(w^1)^TCov(x)w^1,其中可以记S=Cov(x)
(w1)TCov(x)w1,其中可以记S=Cov(x)
问题就变成:
F
i
n
d
w
1
m
a
x
i
m
i
z
i
n
g
(
w
1
)
T
S
w
1
Find\space w^1\space maximizing\space(w^1)^TSw^1
Find w1 maximizing (w1)TSw1
而且还有下面的一个约束条件
w
1
w^1
w1的L2 norm为1:
∣
∣
w
1
∣
∣
2
=
(
w
1
)
T
w
1
=
1
||w^1||_2=(w^1)^Tw^1=1
∣∣w1∣∣2=(w1)Tw1=1
S是covariance matrix,所以他是对称的(Symmetric),还是半正定的(positive-semidefinite)。
所以它的特征值(向量)都是正的。(non-negative eigenvalues)
这里要用到拉格朗日乘子法,写出下面的式子:
g
(
w
1
)
=
(
w
1
)
T
S
w
1
−
α
(
(
w
1
)
T
w
1
−
1
)
g(w^1)=(w^1)TSw^1-\alpha((w^1)^Tw^1-1)
g(w1)=(w1)TSw1−α((w1)Tw1−1)
然后对向量
w
1
w^1
w1的每个分量求偏导,且导数为0。
这里看出来
w
1
w^1
w1是S的特征向量,但是特征向量有很多,现在就要看哪个特征向量可以让上面的式子
(
w
1
)
T
S
w
1
(w^1)^TSw^1
(w1)TSw1最大化:
结论(特征值按降序排列):
接下来解
w
2
w^2
w2:
F
i
n
d
w
2
m
a
x
i
m
i
z
i
n
g
(
w
2
)
T
S
w
2
∣
∣
w
2
∣
∣
2
=
(
w
2
)
T
w
2
=
1
w
2
的
L
2
弄
等
于
1
(
w
2
)
T
w
1
=
0
,
w
2
和
w
1
正
交
Find\space w^2\space maximizing \space(w^2)^TSw^2\\ ||w^2||_2=(w^2)^Tw^2=1\space w^2的L2弄等于1\\ (w^2)^Tw^1=0,w^2和w^1正交
Find w2 maximizing (w2)TSw2∣∣w2∣∣2=(w2)Tw2=1 w2的L2弄等于1(w2)Tw1=0,w2和w1正交
同样用拉格朗日乘子法,写出下面的式子:
g
(
w
2
)
=
(
w
2
)
T
S
w
2
−
α
(
(
w
2
)
T
w
2
−
1
)
−
β
(
(
w
2
)
T
w
1
−
0
)
g(w^2)=(w^2)^TSw^2-\alpha((w^2)^Tw^2-1)-\beta((w^2)^Tw^1-0)
g(w2)=(w2)TSw2−α((w2)Tw2−1)−β((w2)Tw1−0)
同样求偏导,且等于0
上面这里是等式两边同时乘以
(
w
1
)
T
(w^1)^T
(w1)T,有两项变成下面的样子,画红线的这项是一个scalar,所以可以直接转置。
这里S是对称的,所以转置后还是自己,所以上式等于:
把之前的
S
w
1
=
λ
1
w
1
Sw^1=\lambda_1w^1
Sw1=λ1w1带入上式:
所以上面带颜色框框的公式变成:
这个秒解:
β
=
0
\beta=0
β=0,带入:
结论(这里不能选最大那个特征值,因为约束里面说要和
w
1
w^1
w1正交,所以只能选第一大那个):
补充结论
这里PCA做完丢给其他model去训练的时候,新的数据Z是dimension间没有corelationship的,这样可以用比较简单的model来处理Z,原因:
带入后:
S乘进去
w
1
w^1
w1是
S
S
S的最大特征向量
λ
1
\lambda _1
λ1
W插入
w
1
w^1
w1是
W
W
W的第一个row,所以:
e
1
e_1
e1代表一个vector第一维是1,其他是0,以此类推。
PCA的直观说明(举个栗子)
手写的数字是由一些基本的笔画组成用
u
1
,
u
2
,
u
3
,
u
4
,
u
5
.
.
.
u^1,u^2,u^3,u^4,u^5...
u1,u2,u3,u4,u5...表示,些基本的component是vector
把这些vector加起来就得到一个数字:
也就是x等于一堆component的linear combination再加上x的平均:
例如例如上面的7相当于:
c
1
=
1
,
c
2
=
0
,
c
3
=
1
,
c
4
=
0
,
c
5
=
1
c_1=1,c_2=0,c_3=1,c_4=0,c_5=1
c1=1,c2=0,c3=1,c4=0,c5=1:
现在就是要找
u
1
,
u
2
.
.
.
u
K
u^1,u^2...u^K
u1,u2...uK这K个vector,使得
x
^
\widehat x
x
与
x
−
x
ˉ
x-\bar x
x−xˉ越接近越好:
上面的Reconstruction error损失函数可以写为:
蓝色横线部分是
x
^
\hat x
x^.
之前的可以写成:
用PCA找出来的W,实际上就是上面损失函数的解。
这里老师要给出另外一种简单的证明:
这里
c
1
1
c_1^1
c11是
x
1
x^1
x1在
u
1
u^1
u1这个component的weight。
这里
x
1
−
x
ˉ
x^1-\bar x
x1−xˉ是一个vector,它等于component矩阵点乘权重向量。
当然,我们不单单只有
x
1
x^1
x1,还有
x
2
x^2
x2、
x
3
x^3
x3,所以就变成:
左边黄色的矩阵中,列数为数据的数量。要解这个方程,需要用SVD(奇异值分解):李宏毅现代svdPPT
思想就是把上面的矩阵X分解变成下面的样子:
之前PCA求出来的W就是covariance矩阵的最大特征向量,也就是这里的U(
X
X
T
XX^T
XXT就是covariance矩阵),换句话说:W就是这里的component。
由于W是正交矩阵,
c
k
c_k
ck要使得
x
−
x
ˉ
x-\bar x
x−xˉ最小,根据线代的知识得到上式(具体还待补充)。
上面linear combination:
∑
k
=
1
K
c
k
w
k
\sum_{k=1}^Kc_kw^k
∑k=1Kckwk可以看做是NN。
中间的
c
1
c_1
c1的激活函数就是线性函数,
x
−
x
ˉ
x-\bar x
x−xˉ是输入,K=2,所以有两个component:
train这个NN的条件就是要输入和输出越接近越好:
问题:用GD解这个NN得到的W和PCA的解出来的W一样吗?
答:不一样,PCA的W是正交的(因为是用求特征值的方式来求),这里解出来的不一定是正交,但是用NN的好处就是可以加深。
如果有多个隐藏层,那么:
PCA的缺点
非监督
由于非监督,就没有标签,那么对于下面的数据
PCA会这样分:
如果这些数据实际上是这样
这样分投影到这个向量上就会把两个类混起来了!
解决方法就是引入标签,使用线性判别分析(Linear Discriminant Analysis, 简称LDA):
线性
对于一些曲面无法进行处理,例如:
PCA做出来是下面的样子,不是把S形状拉开,而是压扁。(挖坑,后面讲非线性降维)
PCA实作(宝可梦)
Inspired from: https://www.kaggle.com/strakul5/d/abcsds/pokemon/principal-component-analysis-of-pokemon-data
800 Pokemons, 6 features for each (HP, Atk, Def, Sp Atk, Sp Def, Speed)
How many principle components?
答:如果要可视化就要降到二维或者三维,不然六维显示不了,这个数量类似NN的超参数,需要自己定。
根据principle components:
λ
\lambda
λ(就是之前特征向量对应的特征值,这个特征值的意思是在做降维的时候在principle components的dimension上的variance有多大,这个variance就是
λ
\lambda
λ)来定,现在有六个特征,所以covariance matrix是六维,所以可以找到六个特征向量,六个特征值,现在先计算一下每个特征值的比率:
发现第五和第六个特征值比率很小,就是说用这两个dimension来做降维,得到的variance比较小,也就是宝可梦在这两个特征上没有太多的information。因此只要四个principle components。
下面是对这四个components的详解:
第一个component都为正,但值不大,代表强度;
第二个速度为负,防御为正,也就是牺牲速度加强防御;
把这两个components画出来就是上图中的下面部分。
下面是第三和第四个components以及图像:
老师还对outlier做了解释:
PCA实作(手写数字识别)
每个数字都可以拆分成weight:
a
1
a_1
a1乘上component:
w
1
w^1
w1加上。。。。。其实每一个component都是一张28*28的image
下面给出前30个components
也就是用上面的components来做linear combination就可以得到0-9的数字,所以这些components也叫Eigen-digits。
同理,下面是人脸做PCA分解出来的components。
这里有一个问题,按理来说分解的是component,这里明显都是完整的人脸,类似的前面的数字识别找出来的component也不都是笔画,为什么?因为权重可以是正也可以是负数,意味着一个数字9,可以先写8,然后把下面部分去掉(减去),再加一竖。
如果想要得到类似笔画的component,要使用这个技术:Non-negative matrix factorization (NMF)
Non-negative matrix factorization (NMF)
对符号做了规定,只能为正。
文献:Daniel D.Lee and H.Sebastian Seung."Algorithms for non-negative matrix factorization."Advances in neural information processing systems.2001.
所有component的weight:
a
1
,
a
2
.
.
.
a_1,a_2...
a1,a2...都是正的;相当于所有的component都是叠加的效果additive combination。
所有component的每个维度:
w
1
,
w
2
.
.
.
w^1,w^2...
w1,w2...都是正的;相当于每个维度都代表整体的某一个部分。
NMF on MNIST
NMF on Face
矩阵分解Matrix Factorization
下图中的数字代表A-E五个御宅族拥有的二次元人物的图片数量。附上:矩阵分解 BY python
有两个因素促使五个御宅族收集不同的二次元人物:萌傲娇和天然呆。
There are some common factors behind otakus(御宅族) and characters(角色).
注意:萌傲娇和天然呆向量有大小,otakus(御宅族) and characters(角色),拥有的向量相似性特别大就会买之。
The factors are latent.御宅族无人关心,动漫角色角色属性无法直接观察到。
那现在有的关系只是御宅族购买动漫角色的数量:
矩阵中的元素的数字约等于御宅族的向量与二次元人物属性向量的点乘:
找一组
r
A
−
r
B
r^A-r^B
rA−rB乘上
r
1
−
r
4
r^1-r^4
r1−r4得到的矩阵与
M
a
t
r
i
x
X
Matrix\space X
Matrix X越接近越好,这个东西可以用SVD来解。
但是在实际操作过程中,数据往往是下面这个样子,有些缺失值:
一种方式是直接把缺失值用0代替硬做,这个方法不好,具体应该这样:
构造一个损失函数
L
L
L,其中
r
i
r^i
ri是每个御宅族的latent vector,
r
j
r^j
rj是每个动漫角色背后的latent vector,我们要让这两个vector的内积跟购买数量
n
i
j
n_{ij}
nij越接近越好,这里的重点是在求和的过程中只考虑
n
i
j
n_{ij}
nij有值的data,如果
n
i
j
n_{ij}
nij没有值就不算它,然后用GD来求
r
i
r^i
ri和
r
j
r^j
rj,下面是实作:
Assume the dimensions of r are all 2 (there are two factors)
上图中的红框代表AB是萌同一个属性,CDE属于同一个属性(当然每个属性具体代表什么我们无法知晓。只有分析出关联后再来根据动漫人物的属性去判断御宅族的属性),动漫角色也一样。
求出的两个属性矩阵可以预测缺失值(对应属性相乘即可)
(这里和ng在机器学习课程中讲的电影推荐系统原理一样)
Matrix Factorization进阶
上图中的
n
A
1
=
r
A
⋅
r
1
≈
5
n_{A1}=r^A\cdot r^1≈5
nA1=rA⋅r1≈5,实际上可能还有别的因素影响这个数值,也就是这个形式:
r
A
⋅
r
1
+
b
A
+
b
1
≈
5
r^A\cdot r^1+b_A+b_1≈5
rA⋅r1+bA+b1≈5
b
A
b_A
bA代表A御宅族多么喜欢买公仔(不一定是因为喜欢某个人物)
b
1
b_1
b1代表动漫角色1会让人多想购买(不一定因为喜欢这个角色)
损失函数变成:
Matrix Factorization for Topic analysis
这个实例和刚才的二次元例子一样的,只不过做了以下替换:
这里常常会引入权重,这个权重这样考虑,如果一个词例如:“的”出现在多个文档中,该词的权重会很低,如果一个词仅仅在某一个文章中出现,权重会比较高。
Latent factors are topics (財經(投资、股票)、政治 (总统、选举、立委)…… )
其他降维方法
- Multidimensional Scaling (MDS) [Alpaydin, Chapter 6.7]
Only need distance between objects - Probabilistic PCA [Bishop, Chapter 12.2]
- Kernel PCA [Bishop, Chapter 12.3]
non-linear version of PCA - Canonical Correlation Analysis (CCA) [Alpaydin, Chapter 6.9]
- Independent Component Analysis (ICA)
Ref: http://cis.legacy.ics.tkk.fi/aapo/papers/IJCNN99_tutorialweb/ - Linear Discriminant Analysis (LDA) [Alpaydin, Chapter 6.8]
Supervised
在PCA部分,用到了奇异值分解SVD,虽然在数学基础部分有证明,这里还是把老师讲的内容补充进来:
Singular Value Decomposition
Outline
• Diagonalization can only apply on some square matrices.
• Singular value decomposition (SVD) can apply on any matrix.任意一个矩阵可以拆解为三个矩阵相乘。
SVD
SVD可以拆解任意矩阵A(
m
×
n
,
m
≠
n
m\times n,m\ne n
m×n,m=n),不一定非要方形的矩阵。
U和V都是Orthonormal Set(正交)
∑
\sum
∑是Diagonal矩阵(注意这个不是正方形的),The diagonal entries are non-negative,对角线上的值非负。长成这个样子:
里面的
σ
1
>
σ
2
>
σ
3
.
.
.
\sigma_1>\sigma_2>\sigma_3...
σ1>σ2>σ3...,这个可以用交换矩阵的行列的排列来获得这个形式。
What is the rank of A?
先来看
∑
\sum
∑矩阵的秩应该是k(从上图可以看出来)
If A is a
m
×
n
m\times n
m×n matrix, and B is a
n
×
k
n\times k
n×k matrix, then
R
a
n
k
(
A
B
)
≤
m
i
n
(
R
a
n
k
(
A
)
,
R
a
n
k
(
B
)
)
Rank(AB)\leq min(Rank(A),Rank(B))
Rank(AB)≤min(Rank(A),Rank(B))
根据上面的定理我们可以推断
A
=
U
Σ
V
T
A=U\Sigma V^T
A=UΣVT的秩起码是小于等于k
但是这里A的秩是等于k的,因为矩阵左边或者右边乘上independent的矩阵,不改变矩阵的秩。由于U和V都是Orthonormal Set(正交),所以他们两个的列都是independent的,所以A的秩等于k。
于是上面的
∑
\sum
∑可以把为0的列和行盖起来。
然后把U对应
∑
\sum
∑为0的行的列盖起来;把V对应
∑
\sum
∑为0的列的行盖起来:
A就相当于三个比较小的矩阵相乘(注意维度):
上面拿走的是等于0的部分,如果现在我们多拿一点,我们把
∑
\sum
∑中的
σ
k
\sigma_k
σk也拿掉
那么对应的U和V也要多拿掉一些:
当然拿掉后的A’与原来的A不会相等,A’的秩为k-1,A’是世界上秩为k-1与A最接近的矩阵,也就是近似的意思,这个玩意用途就是图片的压缩:
图片(黑白)可以看做是像素的矩阵,然后用SVD的方式做压缩后效果:
https://www.youtube.com/watch?v=pAiVb7gWUrM
SVD之歌:
https://www.youtube.com/watch?v=fKVRSbFKnEw