用Pytorch尝试解决凸优化聚类问题
- 建立问题
- 代码
建立问题
我们知道Pytorch工具可以很方便的对深度神经网络进行训练和建模,但是你是否想过,把Pytorch当成一种解决凸优化问题的工具?今天就带大家简单尝试用Pytorch来解决一个聚类的凸优化问题,希望给大家带来启发。
开始之前,首先给出一个聚类问题的凸优化表达式。对于给定的数据集X∈RN×D\bm{X}\in \mathbb{R}^{N \times D}X∈RN×D,其中NNN表示数据规模,DDD表示数据维度。我们给定一些聚类中心为C∈RZ×D\bm{C} \in \mathbb{R}^{Z \times D}C∈RZ×D,其中ZZZ表示聚类中心的个数。最后给定相似度矩阵S∈RN×Z\bm{S}\in \mathbb{R}^{N \times Z}S∈RN×Z,举例而言,sijs_{ij}sij就表示对于第iii个数据点到第jjj个聚类中心的连接概率,这个数值越接近于1,则表明这个数据点越大概率属于这个聚类中心,否则就说明这个数据点不属于这个聚类中心。
非常显然的,我们给出这个聚类问题的凸优化表达形式:
minC,S∑i=1N∑j=1Zsij∣∣xi−cj∣∣22s.t.siT1=1,sij≥0\min_{\bm{C},\bm{S}} \sum_{i=1}^N \sum_{j=1}^Z s_{ij}\left| \left| \bm{x}_i - \bm{c}_j\right| \right|_2^2\\ s.t.\,\, \bm{s}_i^T\bm{1} = 1, s_{ij} \ge 0 C,Smini=1∑Nj=1∑Zsij∣∣xi−cj∣∣22s.t.siT1=1,sij≥0
但是这个问题的解是非常“硬”(Hard)的,也就是每个样本点都只会去选择离它最近的那个样本中心点,换句话说就是得到的结果si\bm{s}_isi和One-Hot的差不多 (sij∈{0,1}s_{ij} \in \{0,1\}sij∈{0,1})。简单论证一下为什么会出现这样的情况,不妨对第xi\bm{x}_ixi个样本而言其最近的聚类中心是cm\bm{c}_mcm,其次近的聚类中心是cn\bm{c}_ncn。可以证明:
∣∣xi−cm∣∣22≤α∣∣xi−cm∣∣22+(1−α)∣∣xi−cn∣∣22\left| \left| \bm{x}_i - \bm{c}_m\right| \right|_2^2 \le \alpha \left| \left| \bm{x}_i - \bm{c}_m\right| \right|_2^2 + (1-\alpha)\left| \left| \bm{x}_i - \bm{c}_n\right| \right|_2^2 ∣∣xi−cm∣∣22≤α∣∣xi−cm∣∣22+(1−α)∣∣xi−cn∣∣22
其中0≤α≤10 \le \alpha \le 10≤α≤1。为了避免这种情况导致的解的塌陷,一般在其后面加一个正则约束项即可:
minC,S∑i=1N∑j=1Zsij∣∣xi−cj∣∣22+γ∣∣S∣∣22s.t.siT1=1,sij≥0\min_{\bm{C},\bm{S}} \sum_{i=1}^N \sum_{j=1}^Z s_{ij}\left| \left| \bm{x}_i - \bm{c}_j\right| \right|_2^2 + \gamma \left| \left|\bm{S} \right| \right|_2^2 \\ s.t.\,\, \bm{s}_i^T\bm{1} = 1, s_{ij} \ge 0 C,Smini=1∑Nj=1∑Zsij∣∣xi−cj∣∣22+γ∣∣S∣∣22s.t.siT1=1,sij≥0
该优化问题是有闭式解的,但是,今天我们用Pytorch来强行解决它。
代码
下面是喜闻乐见的代码时间
'''
batch_size := 数据样本个数
cluster := 聚类中心个数
feat_dim := 特征的维度
'''
s = torch.rand([batch_size,cluster],requires_grad=True)
c = torch.rand([cluster,feat_dim],requires_grad=True)
上面给出了两个变量的初始化定义。这两个变量在后续过程中需要求取梯度并更新。
for epoch in range(1,50000):'''data: [Batch * Dim]dis_mat: [Batch * Cluster]out: [Batch * Cluster]'''data = Variable(torch_pack(moon_data))dis_mat = get_euclid(data,c)out = torch.nn.functional.softmax(s,dim=1)loss_means = torch.sum(out * dis_mat)loss_reg = torch.norm(out,p=2)loss = loss_means + gamma * loss_reg if epoch % 1000 == 0:print(epoch, loss)loss.backward()s.data.sub_(lr * s.grad.data)c.data.sub_(lr * c.grad.data)s.grad.data.zero_()c.grad.data.zero_()
这里我采用了对si\bm{s}_isi的softmax归一化,其实用其他的归一化应该也是可以的,操作类似。在优化结束之后,得到的outoutout就是我们想要的结果了。