65.9K
CodeProject 正在变化。 阅读更多。
Home

使用潜在语义分析在 3D 中可视化文档

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (12投票s)

2009年5月18日

MIT

4分钟阅读

viewsIcon

45342

downloadIcon

1398

使用潜在语义分析在 3D 中可视化文档。

引言

这里使用 WPF 的 3D 功能来可视化文档集合,在本例中是 AAAI 2014(一个人工智能会议)的已接受论文列表。

潜在语义分析 (LSA) 使用文档/词项矩阵的奇异值分解 (SVD),将文档集合投影到三维潜在空间中。 然后,此空间在 3D 场景中可视化,该场景可以通过拖动鼠标来导航。

该应用程序使用开源 Bright Wire 机器学习库来创建和规范化词项文档矩阵,并使用其相关的线性代数库来执行 SVD 和 LSA。

背景

请参阅我之前的 CodeProject 文章,以简要介绍向量空间模型技术。 主要要点是它们围绕每个文档中每个词项的计数展开,这些计数被规范化然后存储在矩阵中。 然后,可以比较表示文档的列或行,以使用向量乘法来确定相似性。

SVD 的一种描述方式如下:假设您有数千条热带鱼在一个大鱼缸里游动。 您想拍摄一条鱼的照片,该照片可以显示鱼缸中鱼的全部种类,同时保留鱼之间的相对距离。 SVD 将能够告诉您,在任何给定的时刻,拍摄该“最佳”照片的最佳位置和角度。

构建可视化

当应用程序启动时,它会

  1. 下载已接受的论文数据集
  2. 将 CSV 解析为 DataTable
  3. DataTable 创建强类型的 AAAIDocument
  4. 使用每个文档的元数据来创建稀疏特征向量
  5. 规范化稀疏特征向量并创建密集特征向量
var uri = new Uri("https://archive.ics.uci.edu/ml/machine-learning-databases/00307/%5bUCI%5d%20AAAI-14%20Accepted%20Papers%20-%20Papers.csv");
var KEYWORD_SPLIT = " \n".ToCharArray();
var TOPIC_SPLIT = "\n".ToCharArray();
 
// download the document list
var docList = new List<AAAIDocument>();
using (var client = new WebClient()) {
    var data = client.DownloadData(uri);

    Dispatcher.Invoke(() => {
        _statusMessage.Add("Building data table...");
    });

    // parse the file CSV
    var dataTable = new StreamReader(new MemoryStream(data)).ParseCSV(',');
 
    // create strongly typed documents from the data table
    dataTable.ForEach(row => docList.Add(new AAAIDocument {
        Abstract = row.GetField<string>(5),
        Keyword = row.GetField<string>(3).Split(KEYWORD_SPLIT, StringSplitOptions.RemoveEmptyEntries).Select(str => str.ToLower()).ToArray(),
        Topic = row.GetField<string>(4).Split(TOPIC_SPLIT, StringSplitOptions.RemoveEmptyEntries),
        Group = row.GetField<string>(2).Split(TOPIC_SPLIT, StringSplitOptions.RemoveEmptyEntries),
        Title = row.GetField<string>(0)
    }));
}
 
// create a document lookup table
var docTable = docList.ToDictionary(d => d.Title, d => d);
 
// extract features from the document's metadata
var stringTable = new StringTableBuilder();
var classificationSet = new SparseVectorClassificationSet {
    Classification = docList.Select(d => d.AsClassification(stringTable)).ToArray()
};
 
// create dense feature vectors and normalise along the way
var encodings = classificationSet.Vectorise(true);

接下来,将这些密集特征向量组合成一个文档/词项矩阵,并计算其 SVD。

将前三个奇异值和 VT 矩阵的相应行相乘,以创建潜在空间,文档/词项矩阵已投影到该空间中。

在潜在空间上运行 K-means 聚类,以找到相似的文档组,以及与每个群集关联的颜色。

// create a term/document matrix with terms as columns and documents as rows
var matrix = lap.CreateMatrix(vectorList.Select(d => d.Data).ToList());
 
const int K = 3;
var kIndices = Enumerable.Range(0, K).ToList();
var matrixT = matrix.Transpose();
var svd = matrixT.Svd();
 
var s = lap.CreateDiagonal(svd.S.AsIndexable().Values.Take(K).ToList());
var v2 = svd.VT.GetNewMatrixFromRows(kIndices);
using (var sv2 = s.Multiply(v2)) {
    var vectorList2 = sv2.AsIndexable().Columns.ToList();
    var lookupTable2 = vectorList2.Select((v, i) => Tuple.Create(v, vectorList[i])).ToDictionary(d => (IVector)d.Item1, d => lookupTable[d.Item2]);
    var clusters = vectorList2.KMeans(COLOUR_LIST.Length);
    var clusterTable = clusters
        .Select((l, i) => Tuple.Create(l, i))
        .SelectMany(d => d.Item1.Select(v => Tuple.Create(v, d.Item2)))
        .ToDictionary(d => d.Item1, d => COLOUR_LIST[d.Item2])
    ;

然后,创建带有其关联的 AAAIDocument、3D 投影和群集颜色的 Document 模型。 然后规范化文档位置以进行可视化。

var documentList = new List<Document>();
int index = 0;
double maxX = double.MinValue, minX = double.MaxValue, maxY = double.MinValue, minY = double.MaxValue, maxZ = double.MinValue, minZ = double.MaxValue;
foreach (var item in vectorList2) {
    float x = item[0];
    float y = item[1];
    float z = item[2];
    documentList.Add(new Document(x, y, z, index++, lookupTable2[item], clusterTable[item]));
    if (x > maxX)
        maxX = x;
    if (x < minX)
        minX = x;
    if (y > maxY)
        maxY = y;
    if (y < minY)
        minY = y;
    if (z > maxZ)
        maxZ = z;
    if (z < minZ)
        minZ = z;
}
double rangeX = maxX - minX;
double rangeY = maxY - minY;
double rangeZ = maxZ - minZ;
foreach (var document in documentList)
    document.Normalise(minX, rangeX, minY, rangeY, minZ, rangeZ);

最后,每个 Document 都转换为 Cube 并添加到 3D 视口。

var numDocs = documentList.Count;
_cube = new Cube[numDocs];
 
var SCALE = 10;
for(var i = 0; i < numDocs;  i++) {
    var document = documentList[i];
    var cube = _cube[i] = new Cube(SCALE * document.X, SCALE * document.Y, SCALE * document.Z, i);
    cube.Colour = document.Colour;
    viewPort.Children.Add(cube);

使用 3D 模型

3D 场景包含一个定向光,为立方体提供一些额外的深度,以及一个 PerspectiveCamera - 它们的位置都由轨迹球代码转换,以响应鼠标输入。

我们可以使用以下代码对 3D 立方体进行命中测试

Cube foundCube = null;
SearchResult correspondingSearchResult = null;
HitTestResult result = 
   VisualTreeHelper.HitTest(viewPort, e.GetPosition(viewPort));
RayHitTestResult rayResult = result as RayHitTestResult;
if(rayResult != null) {
    RayMeshGeometry3DHitTestResult rayMeshResult = 
            rayResult as RayMeshGeometry3DHitTestResult;
    if(rayMeshResult != null) {
        GeometryModel3D model = 
              rayMeshResult.ModelHit as GeometryModel3D;
        foreach(KeyValuePair<int,> item in _cubeLookup) {
            if(item.Value.Content == model && 
                      _searchResultLookup.TryGetValue(item.Key, 
                      out correspondingSearchResult)) {
                foundCube = item.Value;
                break;
            }
        }
    }
}

然后,可以相应地更新所选/取消选择的立方体和相应搜索结果上的笔刷。

可以通过按住鼠标左键或右键并拖动鼠标来定位 3D 场景。 关于此轨迹球代码的有趣之处在于,鼠标事件在叠加在 3D 场景上的透明边框上触发。 这是因为 WPF 的 Viewport3D 类不会触发鼠标事件,除非光标位于 3D 模型上。 轨迹球代码基本上是一个“黑盒子”,可以附加到任何 3D 场景,以实现场景的可视化操作。 我们按以下方式附加它(请注意,我们正在附加到叠加的边框)

ModelViewer.Trackball trackball = new ModelViewer.Trackball();
myPerspectiveCamera.Transform = trackball.Transform;
directionalLight.Transform = trackball.Transform;
...
trackball.EventSource = borderCapture;

结论

LSA 是一种广泛用于降低数据集维度的技术。 在本例中,通过将其投影到三维空间而不是更典型的二维空间进行可视化,我们保留了更多我们可以实际使用的信息。

使用这种可视化,我们可以看到文档通常遵循相当一致的模式,其中三个主要的文档峰值代表博弈论人类与人工智能以及规划与执行,以及大量描述具体机器学习技术的论文构成文档集合的大部分。

这样的可视化还可以轻松地发现数据集中的异常值。

此技术的主要缺点是 SVD 的计算成本很高。 如果你在 GPU 上运行Bright Wire,你可能会看到性能的提高,但通常 LSA 对于非常大的矩阵来说是不切实际的。

历史

  • 2009 年 5 月 19 日:第一个版本。
  • 2017 年 2 月 23 日:主要修订,更新了数据集的 URL。
© . All rights reserved.