Mesh is Art(7)
基于机器学习的幕墙嵌板优化
前言
上一节我们提到了网格对于幕墙优化有着重要意义,除了平板优化和四点共圆带来的无扭曲结构可以大幅度节省幕墙施工难度和造价以外,还可以通过结合机器学习使用聚类算法来将尺寸和形状近似的网格聚类并优化来节省造价。这一节我们就来讨论一下如何使用聚类算法来优化幕墙嵌板。这个问题具体分为两个大步骤,聚类和优化,需要先将已有的嵌板归为几个大类,每个类别的嵌板的特征相似,然后再对各个类别下的嵌板进行优化,使同一类别中的嵌板的差别尽可能地小。由于本人并没有过多研究机器学习相关内容,故对该部分内容仅作简单介绍。
问题提出
在幕墙设计与建造中,我们通常希望幕墙的嵌板规格能实现标准化,也就是所有幕墙嵌板的规格尺寸完全一样,这样方便批量加工,可以大规模生产。但是当遇到异形复杂的建筑时,规格尺寸完全相同的嵌板难以拟合,在计算机中设计时,无法保证嵌板的平板化,并且嵌板的尺寸难以实现统一,因此只能依赖对大量嵌板的订制来解决问题,这大大增加了施工难度和成本。
本文借助机器学习中的聚类算法,实现了对嵌板的归类,基于归类的基础上,将复杂曲面归为由几类尺寸完全相同的标准化平板嵌板拼接拟合而成,误差在可接受的范围内并且可控。
聚类算法
什么是聚类
首先来介绍下什么是聚类,在wiki中的解释如下:
Cluster analysis or clustering is the task of grouping a set of objects in such a way that objects in the same group (called a cluster) are more similar (in some sense) to each other than to those in other groups (clusters). It is a main task of exploratory data mining, and a common technique for statistical data analysis, used in many fields, including machine learning, pattern recognition, image analysis, information retrieval, bioinformatics, data compression, and computer graphics.
聚类分析或聚类的任务是对一组对象进行分组,使同一组中的对象(称为集群)对比其他组中的对象更相似。它是探索性数据挖掘的主要任务,是统计数据分析的常用技术,常用于机器学习、模式识别、图像分析、信息检索、生物信息学、数据压缩、计算机图形学等诸多领域。
简单来说就是给定一组对象,给定一些特征,把特征相似的对象汇聚在一起。这里需要注意一下分类和聚类的区别:
分类:分类是人为的对对象进行判断,在类别已知的前提下对对象进行归类,是一个有监督的过程。
聚类:聚类是在分类结束之前我们不知道数据如何分类,而是根据算法,自动地将相似的对象归在一起,是一个无监督的过程。、
常用的聚类算法有很多,比如K-Means、K-Modes、Gaussian Mixture Model、Binary Split等等,本文着重讨论K-Means和Gaussian Mixture,并以c#为例,来讨论它的算法和应用。
K-Means聚类
K-means聚类算法是一种通过迭代来求解的算法。使用者需要先指定K个对象作为聚类中心,然后计算每个对象到各个聚类中心之间的距离,直到将所有对象都分配给距离它们各自最近的聚类中心为止。
在.Net中有一个叫Accord.Net的类库(Grasshopper中的Lunchbox插件就是全部使用的这个类库),它提供了统计分析、机器学习、图像处理、计算机视觉相关的算法,该框架被分成了多个程序集,我们可以直接去官网下载或者使用NuGet得到。下面我们直接使用这个类库做几个案例来说明这个算法。
示例一
我们先举一个简单的例子,做一个关于x-y平面上点集的聚类。实现步骤如下:
首先,为了方便,我们可以设置一个随机种子来从数据中随机取K个点作为聚类中心点(也可以手动设置),根据输入K值(分组的数量)来初始化K-Means类。然后我们就可以开始定义聚类特征,为了方便后面其他数据类型的理解,这里我们多做一步,先将点的x、y坐标放到一个树形数据(DataTree)的不同分支中,再将这个DataTree整理成二维数组的形式。最后将整理好的数据喂给K-Means类求解即可。
代码实现如下:
private void RunScript(int k, List<Point3d> pt, ref object A)
{
//设置随机种子
Accord.Math.Random.Generator.Seed = 0;
//初始化K-Means
Accord.MachineLearning.KMeans kmeans = new Accord.MachineLearning.KMeans(k);
//定义聚类特征
DataTree<double> TreeToCluster = new DataTree<double>();
//将顶点的x和y值分别定义到一个树形数据的两个分支中
for(int i = 0; i < pt.Count; ++i)
{
TreeToCluster.Add(pt[i].X, new GH_Path(i));
TreeToCluster.Add(pt[i].Y, new GH_Path(i));
}
//整理数据,将DataTree转换为二维数组
int n_attr = 2;
double[][] ToCluster = new double[pt.Count][];
for(int i = 0; i < TreeToCluster.BranchCount; ++i)
{
double[] ToCluster_temp = new double[n_attr];
//将分支中的每个数据输入进二维数组
for(int j = 0; j < TreeToCluster.Branch(i).Count; ++j)
{
ToCluster_temp[j] = TreeToCluster.Branch(i)[j];
}
ToCluster[i] = ToCluster_temp;
}
//求解K-Means
Accord.MachineLearning.KMeansClusterCollection clusters = kmeans.Learn(ToCluster);
int[] labels = clusters.Decide(ToCluster);
//输出分类索引
A = labels;
}
最后得到的数据是分好类的顶点索引,我们只需要按照索引从原数据中依次取出即可实