掘金 人工智能 05月28日 16:23
AI玩游戏的一点尝试(2)—— 初探无监督学习与特征可视化
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨了使用AI玩游戏的一种尝试,重点在于架构设计和初步状态识别。文章首先介绍了使用模型提取图片特征的方法,并尝试使用DBSCAN进行聚类,但效果不理想。随后,作者调整参数,获得了较好的聚类效果,但也发现了问题,如相似状态因画面差异被分为不同簇。为了解决这个问题,作者尝试将两个界面分开,并使用K-means进行聚类,成功区分了训练界面和养成主界面。最后,作者提到数据量增大导致训练减缓,计划清洗数据集并改进状态识别模型。

🖼️ 通过卷积神经网络(CNN)提取游戏画面的特征,文章使用了包含卷积层和ReLU激活函数的编码器(encoder)与包含转置卷积层和ReLU激活函数的解码器(decoder),旨在提取图像的关键特征。

📊 尝试使用DBSCAN聚类算法对游戏状态进行分类,但初始参数下,大部分图片被识别为噪声。经过调整参数后,聚类效果有所改善,但仍存在同一状态被分为不同簇的问题,且聚类结果不稳定,需要人工干预。

✂️ 针对DBSCAN聚类效果不佳的问题,文章提出将游戏界面进行裁剪,仅保留差异部分,并使用K-means聚类算法。实验结果表明,K-means能够有效区分训练界面和养成主界面。

📉 数据集清洗是关键步骤,作者提到由于数据采集频率高,数据集中存在大量重复图片,导致训练速度减缓。因此,下一步计划对数据集进行清洗,以提高训练效率。

前言

AI玩游戏的一点尝试(1)—— 架构设计与初步状态识别

无监督学习

使用模型提取图片特征:

        self.encoder = nn.Sequential(            nn.Conv2d(3, 32, 3, stride=2, padding=1),  # 320x180 → 160x90            nn.ReLU(),            nn.Conv2d(32, 64, 3, stride=2, padding=1),  # 160x90 → 80x45            nn.ReLU(),            nn.Conv2d(64, 128, 3, stride=2, padding=1),  # 80x45 → 40x23            nn.ReLU(),            nn.Conv2d(128, 256, 3, stride=2, padding=1), # 40x23 → 20x12            nn.ReLU()        )                self.decoder = nn.Sequential(            nn.ConvTranspose2d(256, 128, 3, stride=2, padding=1, output_padding=1), # 20x12 → 40x23            nn.ReLU(),            nn.ConvTranspose2d(128, 64, 3, stride=2, padding=1, output_padding=1),  # 40x23 → 80x45            nn.ReLU(),            nn.ConvTranspose2d(64, 32, 3, stride=2, padding=1, output_padding=1),   # 80x45 → 160x90            nn.ReLU(),            nn.ConvTranspose2d(32, 3, 3, stride=2, padding=1, output_padding=1),    # 160x90 → 320x180            nn.Sigmoid()        )

使用DBSCAN进行聚类:

scaler = StandardScaler()features_scaled = scaler.fit_transform(features)dbscan = DBSCAN(    eps=eps,    min_samples=min_samples,    metric='euclidean',    n_jobs=-1)

然而对于默认参数eps=0.5, min_samples=5来说,聚类效果并不理想:

INFO:cluster_all_images:聚类结果:INFO:cluster_all_images:  发现的簇数量: 3INFO:cluster_all_images:  噪声点数量: 4677INFO:cluster_all_images:  噪声点比例: 97.62%INFO:cluster_all_images:  簇 0 的样本数: 63 (1.31%)INFO:cluster_all_images:  簇 1 的样本数: 40 (0.83%)INFO:cluster_all_images:  簇 2 的样本数: 11 (0.23%)

绝大部分的图片都被归为了噪声,成簇的样本是一些完全一样的无关紧要的图片:

经过多次尝试,eps=187的时候有比较好的聚类效果:

INFO:cluster_all_images:聚类结果:INFO:cluster_all_images:  发现的簇数量: 85INFO:cluster_all_images:  噪声点数量: 630INFO:cluster_all_images:  噪声点比例: 13.15%INFO:cluster_all_images:  簇 0 的样本数: 13 (0.27%)INFO:cluster_all_images:  簇 1 的样本数: 6 (0.13%)INFO:cluster_all_images:  簇 2 的样本数: 5 (0.10%)INFO:cluster_all_images:  簇 3 的样本数: 157 (3.28%)INFO:cluster_all_images:  簇 4 的样本数: 27 (0.56%)INFO:cluster_all_images:  簇 5 的样本数: 21 (0.44%)···INFO:cluster_all_images:  簇 84 的样本数: 25 (0.52%)

此时模型可以把大部分相似的图片聚类为一个簇:

但是同时也发现了问题:

从状态理解上来说,上面都是处于不需要操作的对话状态,但是因为人物和背景的不同,导致画面有较大差异,被聚类成了两个簇。

另一方面,每次进行聚类分簇的结果是不确定的,模型并不能保证上一次的簇1和下一次的簇1相似,因此聚类后还是需要人工去辨别重新整合分类。

特征可视化

于是编写了一个脚本查看每层的特征图:

可以发现相比于经典的图像识别模型,这个模型几乎没有提取到有用的特征,画面的无关数据与关联数据没有区分开来(也有原因是数据集中我都是用一只马娘训练的)

顺便看了看上个状态识别模型的特征图:

(看归看先用着效果不好再解决)

继续无监督学习

既然一次性分类所有截图不太可能,把两个界面分开感觉还是有希望的。刚好上个状态识别模型中,我把养成主界面和训练主界面放在了一起,这次看看能不能把他们通过无监督学习分开来:

对于这两个界面来说,最大的区别在于下半部分的按钮区域,于是先对数据进行裁剪,只保留不一样的部分:

x, y, w, h = self.training_areaimage = image.crop((x, y, x + w, y + h))

使用Kmeans进行聚类:

with torch.no_grad():        for images, paths in data_loader:            images = images.to(device)            encoded, _ = model(images)            # 展平特征            encoded = encoded.view(encoded.size(0), -1)  # [batch_size, 256*20*12]            features.extend(encoded.cpu().numpy())            image_paths.extend(paths)        features = np.array(features)  # [N, 256*20*12]    logger.info(f"原始特征维度: {features.shape}")        # 移除零方差特征    from sklearn.feature_selection import VarianceThreshold    selector = VarianceThreshold(threshold=0.0)    features = selector.fit_transform(features)    logger.info(f"移除零方差特征后的维度: {features.shape}")        # 标准化特征    from sklearn.preprocessing import StandardScaler    scaler = StandardScaler()    features = scaler.fit_transform(features)        # 使用PCA降维    from sklearn.decomposition import PCA    # 保留95%的方差    pca = PCA(n_components=0.95)    features = pca.fit_transform(features)    logger.info(f"PCA降维后的维度: {features.shape}")    logger.info(f"保留的方差比例: {sum(pca.explained_variance_ratio_):.3f}")        logger.info(f"最终特征值范围: [{features.min():.3f}, {features.max():.3f}]")        # 使用更稳定的KMeans参数    from sklearn.cluster import KMeans    kmeans = KMeans(        n_clusters=2,        random_state=42,        n_init=20,  # 增加初始化次数        max_iter=500,  # 增加最大迭代次数        tol=1e-4,  # 调整收敛阈值        algorithm='lloyd'  # 使用Lloyd算法,更稳定但更慢    )
INFO:cluster_images:原始特征维度: (1198, 61440)INFO:cluster_images:移除零方差特征后的维度: (1198, 51883)INFO:cluster_images:PCA降维后的维度: (1198, 336)INFO:cluster_images:保留的方差比例: 0.950INFO:cluster_images:最终特征值范围: [-290.572, 872.163]INFO:cluster_images:聚类完成,轮廓系数: 0.174INFO:cluster_images:聚类惯性: 49129444.000INFO:cluster_images:类别 0 的样本数: 861INFO:cluster_images:类别 1 的样本数: 337

效果很好,训练界面和养成主界面被完美的区分开了:

这样就可以进一步用标注数据去训练状态识别模型了。

下一步

数据量越来越多导致训练速度逐渐减缓,而因为数据是每秒采集一次的,数据集中有大量重复的图片,下一步准备先对数据集进行一次清洗再改进状态识别模型。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AI游戏 状态识别 无监督学习 聚类算法
相关文章