机器学习:DEC

要谈DEC算法,首先得从问题背景出发,DEC算法其实同k-means一样,都是一种聚类算法,属于无监督学习的一种。那我们为什么要引入这么一个算法,道理也简单,自然是我们之前的算法有其局限性。

        聚类算法中的一个核心便是距离函数,可以是欧氏距离,也可以是特征之间的某种关系,但k-means等算法对距离函数的处理往往局限于原始数据空间,这也就导致,如同组会上学姐学长所说,输入维度较高时,往往无效。而DEC聚类算法则解决了这一问题,这得益于它的设计框架和独特的实现过程。

       DEC通过同时学习特征空间Z中的k个聚类中心和将数据点映射到Z的DNN的参数 θ 来聚类数据。

          DEC具有两个阶段:( 1)使用自动编码器进行参数初始化(AE),

                                         (2)参数优化(即聚类),其中我们在计算辅助目标分布和最小化Kullback-Leibler(KL)散度之间进行迭代。

如上图:

1. 使用深度学习自编码进行参数初始化

2.选取AE模型中的Encoder部分,加入聚类层,使用KL散度进行训练聚类

 即先将特征压缩,构造软分布和辅助分布,(p,q)

迭代过程如下

 然后将梯度∂L/∂zi向下传递到DNN,并在标准反向传播中用于计算DNN的参数梯度∂L/∂θ。为了发现聚类分配,两次迭代之间更改聚类分配的点小于tol%点时,我们将停止。

代码注解:

def target_distribution(q):
    weight = q ** 2 / q.sum(0)
    return (weight.t() / weight.sum(1)).t()

此句对应

 

 q = 1.0 / (1.0 + torch.sum(torch.pow(z.unsqueeze(1) - self.cluster_layer, 2), 2) / self.v)
        q = q.pow((self.v + 1.0) / 2.0)
        q = (q.t() / torch.sum(q, 1)).t()

此句对应

 其中 i表示第i样本,j表示第j个聚类中心, z表示原始特征分布经过Encoder之后的表征空间。qij可以解释为样本i属于聚类j的概率

代码:

AE版块

class AE(nn.Module):

    def __init__(self, n_enc_1, n_enc_2, n_enc_3, n_dec_1, n_dec_2, n_dec_3,
                 n_input, n_z):
        super(AE, self).__init__()
        self.enc_1 = Linear(n_input, n_enc_1)
        self.enc_2 = Linear(n_enc_1, n_enc_2)
        self.enc_3 = Linear(n_enc_2, n_enc_3)
        self.z_layer = Linear(n_enc_3, n_z)

        self.dec_1 = Linear(n_z, n_dec_1)
        self.dec_2 = Linear(n_dec_1, n_dec_2)
        self.dec_3 = Linear(n_dec_2, n_dec_3)
        self.x_bar_layer = Linear(n_dec_3, n_input)

    def forward(self, x):
        enc_h1 = F.relu(self.enc_1(x))
        enc_h2 = F.relu(self.enc_2(enc_h1))
        enc_h3 = F.relu(self.enc_3(enc_h2))
        z = self.z_layer(enc_h3)

        dec_h1 = F.relu(self.dec_1(z))
        dec_h2 = F.relu(self.dec_2(dec_h1))
        dec_h3 = F.relu(self.dec_3(dec_h2))
        x_bar = self.x_bar_layer(dec_h3)

        return x_bar, enc_h1, enc_h2, enc_h3, z

SDCN:

class SDCN(nn.Module):

    def __init__(self, n_enc_1, n_enc_2, n_enc_3, n_dec_1, n_dec_2, n_dec_3,
                 n_input, n_z, n_clusters, v=1):
        super(SDCN, self).__init__()

        # autoencoder for intra information
        self.ae = AE(
            n_enc_1=n_enc_1,
            n_enc_2=n_enc_2,
            n_enc_3=n_enc_3,
            n_dec_1=n_dec_1,
            n_dec_2=n_dec_2,
            n_dec_3=n_dec_3,
            n_input=n_input,
            n_z=n_z)
        self.ae.load_state_dict(torch.load(args.pretrain_path, map_location='cpu'))


        # cluster layer
        self.cluster_layer = Parameter(torch.Tensor(n_clusters, n_z))
        torch.nn.init.xavier_normal_(self.cluster_layer.data)

        # degree
        self.v = v

    def forward(self, x):
        # DNN Module
        x_bar, tra1, tra2, tra3, z = self.ae(x)

        # Dual Self-supervised Module
        q = 1.0 / (1.0 + torch.sum(torch.pow(z.unsqueeze(1) - self.cluster_layer, 2), 2) / self.v)
        q = q.pow((self.v + 1.0) / 2.0)
        q = (q.t() / torch.sum(q, 1)).t()

        return x_bar, q, z

辅助分布;

def target_distribution(q):
    weight = q ** 2 / q.sum(0)
    return (weight.t() / weight.sum(1)).t()

在聚类层中优化参数和迭代:

def train_sdcn(dataset):
    model = SDCN(500, 500, 2000, 2000, 500, 500,
                 n_input=args.n_input,
                 n_z=args.n_z,
                 n_clusters=args.n_clusters,
                 v=1.0).to(device)
    print(model)

    optimizer = Adam(model.parameters(), lr=args.lr)

    # cluster parameter initiate
    data = torch.Tensor(dataset.x).to(device)
    y = dataset.y
    with torch.no_grad():
        _, _, _, _, z = model.ae(data)

    kmeans = KMeans(n_clusters=args.n_clusters, n_init=20)
    y_pred = kmeans.fit_predict(z.data.cpu().numpy())
    y_pred_last = y_pred
    model.cluster_layer.data = torch.tensor(kmeans.cluster_centers_).to(device)
    eva(y, y_pred, 'pae')

    for epoch in range(200):
        if epoch % 1 == 0:
            # update_interval
            _, tmp_q, _ = model(data)
            tmp_q = tmp_q.data
            p = target_distribution(tmp_q)

            res1 = tmp_q.cpu().numpy().argmax(1)  # Q
            res3 = p.data.cpu().numpy().argmax(1)  # P
            eva(y, res1, str(epoch) + 'Q')
            eva(y, res3, str(epoch) + 'P')

        x_bar, q, _ = model(data)

        kl_loss = F.kl_div(q.log(), p, reduction='batchmean')
        re_loss = F.mse_loss(x_bar, data)


        loss = kl_loss + re_loss

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

参考:

1.DEC原理一篇就好 - 知乎 (zhihu.com)

2.无监督的深度嵌入式聚类(2016年论文) - 知乎 (zhihu.com)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值