戰(zhàn):MNIST 手寫(xiě)數(shù)字二維降維可視化)
示例工程【免費(fèi)下載鏈接】tfjs-examplesExamples built with TensorFlow.js項(xiàng)目地址https://gitcode.com/gh_mirrors/tf/tfjs-examples點(diǎn)擊查看免費(fèi)下載本指南圍繞 tfjs-examples 倉(cāng)庫(kù)中的tsne-mnist-canvas示例講解如何借助 TensorFlow.js 生態(tài)下的 tfjs-tsne 庫(kù)在瀏覽器中完成 MNIST 手寫(xiě)數(shù)字?jǐn)?shù)據(jù)集的 t-SNE 降維與實(shí)時(shí)可視化。讀完本文你將掌握 MNIST 數(shù)據(jù)的加載與張量預(yù)處理管線、tfjs-tsne 的 kNN 與迭代式嵌入 API、基于 d3 與 Canvas 2D 的坐標(biāo)渲染方案以及 WebGL 2 環(huán)境要求與關(guān)鍵參數(shù)的調(diào)優(yōu)方向。示例概覽在瀏覽器里跑通完整的 t-SNE 流程正如 README.md 所描述的本示例演示了使用tfjs-tsne庫(kù)對(duì) MNIST 數(shù)據(jù)集 做降維從測(cè)試集中取出10000 張手寫(xiě)數(shù)字圖片將其縮放為10×10 像素再用t-SNE 算法把它們組織到二維平面上讓相同數(shù)字在空間上聚成簇、不同數(shù)字彼此分離從而直觀地檢驗(yàn)高維圖像數(shù)據(jù)在低維空間中的結(jié)構(gòu)。演示頁(yè)面的標(biāo)題正是“GPU Accelerated T-SNE in the Browser”整個(gè)計(jì)算過(guò)程依托 TensorFlow.js 與 WebGL 在 GPU 上完成這也是本示例區(qū)別于傳統(tǒng) CPU 版 t-SNE 工具的核心亮點(diǎn)。該示例的倉(cāng)庫(kù)結(jié)構(gòu)十分精簡(jiǎn)只有 6 個(gè)文件tsne-mnist-canvas/ ├── README.md # 示例說(shuō)明與運(yùn)行命令 ├── index.html # 頁(yè)面結(jié)構(gòu)、參數(shù)控件與樣式 ├── index.js # 主邏輯數(shù)據(jù)預(yù)處理、t-SNE 調(diào)用、渲染 ├── data.js # MnistData 類(lèi)MNIST 數(shù)據(jù)加載與切分 ├── package.json # 依賴(lài)與構(gòu)建腳本 └── yarn.lock # 依賴(lài)鎖定文件運(yùn)行環(huán)境前提必須支持 WebGL 2README 給出了一個(gè)重要但容易被忽略的環(huán)境限制tfjs-tsne requires WebGL 2 support and thus will not work on certain devices, mobile devices especially. Currently it best works on desktop devices.也就是說(shuō)tfjs-tsne依賴(lài)WebGL 2支持因此移動(dòng)設(shè)備上很可能無(wú)法運(yùn)行尤其是部分只支持 WebGL 1 的手機(jī)瀏覽器桌面端瀏覽器體驗(yàn)最佳建議在較新的 Chrome / Firefox / Edge 等支持 WebGL 2 的桌面瀏覽器中運(yùn)行。從實(shí)現(xiàn)上可以推斷該限制源于 tfjs-tsne 將 kNN 近鄰圖構(gòu)建與 t-SNE 優(yōu)化等重計(jì)算全部放在 GPU 著色器shader中執(zhí)行以獲得實(shí)時(shí)的迭代可視化體驗(yàn)而 WebGL 2 提供了所需的更靈活的浮點(diǎn)紋理與計(jì)算能力。因此如果運(yùn)行頁(yè)面時(shí)出現(xiàn)空白或報(bào)錯(cuò)請(qǐng)先確認(rèn)瀏覽器支持 WebGL 2??焖龠\(yùn)行兩條命令啟動(dòng)README 給出的啟動(dòng)方式只有兩條命令yarn yarn watchyarn安裝依賴(lài)依賴(lài)清單見(jiàn) package.jsontensorflow/tfjs^1.3.2、tensorflow/tfjs-tsne0.2.0、d3^5.2.0yarn watch啟動(dòng)開(kāi)發(fā)服務(wù)器并在瀏覽器中自動(dòng)打開(kāi)頁(yè)面官方同時(shí)在 GCS 上托管了在線演示版本可直接訪問(wèn)體驗(yàn)。package.json中完整定義了三個(gè)腳本可滿足開(kāi)發(fā)與構(gòu)建兩種場(chǎng)景scripts: { watch: NODE_ENVdevelopment parcel index.html --no-hmr --open, build: NODE_ENVproduction parcel build --no-scope-hoist index.html --public-url ./, link-local: yalc link }yarn watch基于Parcel的本地開(kāi)發(fā)模式--no-hmr關(guān)閉熱模塊替換避免開(kāi)發(fā)中頻繁整頁(yè)刷新--open自動(dòng)打開(kāi)瀏覽器yarn build產(chǎn)出生產(chǎn)構(gòu)建--public-url ./保證資源使用相對(duì)路徑方便部署到任意靜態(tài)目錄yarn link-local配合 devDependencies 中的yalc用于本地鏈接尚未發(fā)布的 tfjs-tsne 版本屬于庫(kù)作者聯(lián)調(diào)場(chǎng)景。數(shù)據(jù)準(zhǔn)備從 MNIST 精靈圖加載訓(xùn)練與測(cè)試集數(shù)據(jù)層由 data.js 中的MnistData類(lèi)負(fù)責(zé)它直接從云存儲(chǔ)加載兩份 MNIST 原始資源一張把全部 65000 張圖片縱向拼接的精靈圖sprite以及一份 uint8 格式的標(biāo)簽文件。關(guān)鍵常量見(jiàn) data.jsconst IMAGE_SIZE 784; // 28×28 784 像素 const NUM_CLASSES 10; // 0~9 十個(gè)數(shù)字類(lèi)別 const NUM_DATASET_ELEMENTS 65000; // 全量樣本數(shù) const NUM_TRAIN_ELEMENTS 55000; // 訓(xùn)練集 const NUM_TEST_ELEMENTS 10000; // 測(cè)試集load()方法的核心加載流程值得注意用Image對(duì)象加載精靈圖并設(shè)置crossOrigin 以跨域讀取像素以chunkSize 5000為步長(zhǎng)分塊繪制到臨時(shí) canvas 上再通過(guò)getImageData取出像素——由于 MNIST 是灰度圖只需讀取紅色通道并除以 255 完成歸一化見(jiàn) data.js并行fetch標(biāo)簽文件解析為Uint8Array用tf.util.createShuffledIndices生成亂序索引并將圖像與標(biāo)簽按 55000 / 10000 切分為trainImages、testImages、trainLabels、testLabels見(jiàn) data.js。本示例只用到測(cè)試集因此在 index.js 中僅需一行async function loadData() { data new MnistData(); return data.load(); }預(yù)處理張量管線28×28 圖像降維到 10×10 特征這是整個(gè)示例中最有工程價(jià)值的一段在送入 t-SNE 之前把高維輸入壓縮到可控維度。start函數(shù)見(jiàn) index.js在tf.tidy作用域內(nèi)完成全部張量操作const [reshaped, labels] tf.tidy(() { // 1. 標(biāo)簽one-hot 轉(zhuǎn)成數(shù)字?jǐn)?shù)組 const labelsTensor tf.tensor2d(data.testLabels, [NUM_IMAGES, NUM_CLASSES]); const labels labelsTensor.argMax(1).dataSync(); // 2. 圖像構(gòu)建 [10000, 28, 28, 1] 張量并截取前 numPoints 張 const images tf.tensor4d(data.testImages, [ NUM_IMAGES, IMG_HEIGHT, IMG_WIDTH, 1 ]).slice([0], [numPoints]); // 3. 縮放雙線性插值到 10×10 const resized images.resizeBilinear([NEW_HEIGHT, NEW_WIDTH]); // 4. 重塑攤平為 tfjs-tsne 期望的 rank-2 輸入 const reshaped resized.reshape([numPoints, NEW_HEIGHT * NEW_WIDTH]); return [reshaped, labels]; });各步驟的作用標(biāo)簽轉(zhuǎn)換MNIST 標(biāo)簽是 one-hot 編碼的[10000, 10]張量用argMax(1)取每行最大值下標(biāo)得到[10000]的數(shù)字標(biāo)簽數(shù)組供后續(xù)按類(lèi)別著色切片slice([0], [numPoints])從測(cè)試集中取用戶(hù)指定的樣本數(shù)默認(rèn) 10000雙線性縮放resizeBilinear([10, 10])把每張 28×28 圖像縮小到 10×10將特征維度從 784 降到100這既大幅降低了 t-SNE 的計(jì)算量又不至于損失過(guò)多的數(shù)字結(jié)構(gòu)信息重塑reshape([numPoints, 100])得到 tfjs-tsne 要求的 rank-2 輸入張量[樣本數(shù), 特征數(shù)]。最后tf.tidy自動(dòng)回收中間張量函數(shù)末尾還顯式調(diào)用reshaped.dispose()釋放輸入張量避免 GPU 顯存泄漏——在動(dòng)輒 10000 行樣本的降維任務(wù)里內(nèi)存管理是保持頁(yè)面流暢的必要細(xì)節(jié)。t-SNE 計(jì)算流程tfjs-tsne 的核心調(diào)用鏈doEmbedding函數(shù)見(jiàn) index.js完整展示了 tfjs-tsne 的異步 API 用法整個(gè)流程分為兩階段async function doEmbedding( data, labels, numIterations, knnIter, perplexity, onIteration) { // 階段一創(chuàng)建 embedder 并預(yù)計(jì)算 kNN 近鄰圖 const embedder tsne.tsne(data); await embedder.iterateKnn(knnIter); // 階段二逐輪迭代優(yōu)化并在每輪之間渲染當(dāng)前坐標(biāo) for (let i 0; i numIterations; i) { await embedder.iterate(1); const coordinates await embedder.coordsArray(); onIteration(coordinates, labels); await tf.nextFrame(); } }逐行拆解tsne.tsne(data)傳入[樣本數(shù), 100]的張量創(chuàng)建一個(gè) GPU 加速的 t-SNE embedder 實(shí)例await embedder.iterateKnn(knnIter)正式優(yōu)化前先執(zhí)行若干輪 kNNk 近鄰迭代構(gòu)建樣本之間的近鄰關(guān)系圖這是 t-SNE 計(jì)算相似度矩陣的基礎(chǔ)await embedder.iterate(1)執(zhí)行一輪 t-SNE 梯度優(yōu)化迭代。示例刻意每輪只迭代 1 次代碼注釋明確說(shuō)明“你也可以在兩次取坐標(biāo)渲染之間多迭代幾次”——這樣可以在迭代尚未收斂時(shí)就展示中間狀態(tài)形成漸進(jìn)式動(dòng)畫(huà)await embedder.coordsArray()從 GPU 取回當(dāng)前所有樣本的二維坐標(biāo)數(shù)組交給渲染回調(diào)await tf.nextFrame()把控制權(quán)交還給瀏覽器渲染線程避免長(zhǎng)循環(huán)阻塞頁(yè)面是瀏覽器端異步計(jì)算中保持 UI 流暢的關(guān)鍵一步。實(shí)時(shí)渲染d3 與 Canvas 2D 畫(huà)出手寫(xiě)數(shù)字散點(diǎn)renderEmbedding見(jiàn) index.js負(fù)責(zé)把坐標(biāo)繪制到 800×800 的 canvas 上采用d3 縮放 Canvas 2D 逐點(diǎn)繪制的組合function renderEmbedding(coordinates, labels) { const x d3.scaleLinear().range([0, width]).domain([0, 1]); const y d3.scaleLinear().range([0, height]).domain([0, 1]); context.clearRect(0, 0, width, height); coordinates.forEach(function(d, i) { context.font 10px sans; context.fillStyle colors(parseInt(labels[i], 10)); context.fillText(labels[i], x(d[0]), y(d[1])); }); }渲染細(xì)節(jié)坐標(biāo)映射d3.scaleLinear把坐標(biāo)從[0, 1]區(qū)間線性映射到畫(huà)布像素范圍——從 domain 取值可以推斷coordsArray()返回的坐標(biāo)被歸一化在[0, 1]內(nèi)逐點(diǎn)著色colors d3.scaleOrdinal(d3.schemeCategory10)見(jiàn) index.js為 10 個(gè)數(shù)字類(lèi)別各分配一種顏色直接繪制數(shù)字字符每個(gè)樣本用fillText繪制它自身的標(biāo)簽數(shù)字0~9因此不依賴(lài)額外的點(diǎn)/圖片資源幾萬(wàn)點(diǎn)也能在 10px 字號(hào)下保持清晰的散點(diǎn)分布逐幀重繪每輪 t-SNE 迭代都會(huì)clearRect后重新繪制全部坐標(biāo)可視化出“點(diǎn)云逐步聚成 10 團(tuán)”的動(dòng)態(tài)過(guò)程。canvas 本身由initVisualization在#vis容器內(nèi)動(dòng)態(tài)創(chuàng)建并獲取 2d context見(jiàn) index.js樣式上通過(guò) 1px 黑色邊框與頁(yè)面布局區(qū)分可視化區(qū)域。交互控件與參數(shù)調(diào)優(yōu)index.html 提供了一組滑塊與單選按鈕initControls見(jiàn) index.js在數(shù)據(jù)加載完成后將其啟用。點(diǎn)擊Start T-SNE按鈕后讀取全部參數(shù)并調(diào)用start(numPoints, tsneIter, knnIter, perplexity)按鈕文字隨之變?yōu)镽estart TSNE可反復(fù)調(diào)整參數(shù)重跑。全部可調(diào)參數(shù)及默認(rèn)值如下控件含義取值范圍默認(rèn)值Perplexityt-SNE 的核心超參數(shù)控制每個(gè)點(diǎn)周?chē)坝行Ы彙钡臄?shù)量用于平衡局部與全局結(jié)構(gòu)2 ~ 42步長(zhǎng) 130KNN Iterations正式優(yōu)化前構(gòu)建 kNN 近鄰圖的迭代輪數(shù)決定相似度矩陣的質(zhì)量2 ~ 1500步長(zhǎng) 1800T-SNE Iterations進(jìn)入 t-SNE 優(yōu)化階段后的總迭代輪數(shù)決定嵌入結(jié)果的收斂程度1 ~ 1000步長(zhǎng) 1500Number of Digits實(shí)際參與降維的樣本數(shù)對(duì)應(yīng)numPoints參數(shù)500 / 2000 / 10000單選10000調(diào)參方向可以這樣把握以下為 t-SNE 通用經(jīng)驗(yàn)可作為調(diào)試參考Perplexity從算法原理看值越大越側(cè)重保留全局結(jié)構(gòu)值越小越側(cè)重局部結(jié)構(gòu)對(duì) 500 個(gè)樣本的小數(shù)據(jù)集過(guò)大的 perplexity 會(huì)引入偏差通??上鄳?yīng)調(diào)低KNN Iterations迭代越多近鄰圖越準(zhǔn)確但耗時(shí)線性增長(zhǎng)。若聚類(lèi)結(jié)果明顯雜亂可優(yōu)先增大該項(xiàng)T-SNE Iterations迭代越多收斂越好但每輪都要在 GPU 上更新并回讀坐標(biāo)。本示例每輪只iterate(1)并立即渲染因此總迭代數(shù)也直接決定動(dòng)畫(huà)時(shí)長(zhǎng)樣本數(shù)10000 點(diǎn)最能展現(xiàn) MNIST 的完整結(jié)構(gòu)但計(jì)算與渲染開(kāi)銷(xiāo)最大調(diào)試階段可用 500 或 2000 快速驗(yàn)證參數(shù)效果。另外控件區(qū)域的所有input[typerange]滑塊都會(huì)在滑動(dòng)時(shí)實(shí)時(shí)把當(dāng)前值回顯到其前的span標(biāo)簽中見(jiàn) index.js方便觀察所選數(shù)值。小結(jié)一條完整的瀏覽器端降維鏈路tsne-mnist-canvas用約百行核心代碼串起了從數(shù)據(jù)加載、張量預(yù)處理、GPU 加速 t-SNE 到 Canvas 實(shí)時(shí)渲染的完整鏈路是理解 tfjs-tsne 用法的理想起點(diǎn)?;仡櫛疚囊c(diǎn)環(huán)境tfjs-tsne 依賴(lài) WebGL 2移動(dòng)端兼容性有限桌面瀏覽器體驗(yàn)最佳數(shù)據(jù)data.js 展示了從精靈圖異步加載 MNIST 并進(jìn)行 train/test 切分的完整寫(xiě)法預(yù)處理index.js 通過(guò)slice → resizeBilinear → reshape把 28×28 圖像壓縮為 10×10100 維特征并借助tf.tidy管理 GPU 內(nèi)存計(jì)算tsne.tsne → iterateKnn → iterate(1) → coordsArray → tf.nextFrame兩階段調(diào)用鏈兼顧了收斂質(zhì)量與 UI 流暢度渲染d3 比例尺 Canvas 逐點(diǎn)fillText的方案支持?jǐn)?shù)萬(wàn)點(diǎn)的實(shí)時(shí)動(dòng)畫(huà)。如需深入可繼續(xù)閱讀倉(cāng)庫(kù)中 data.js、index.js 與 index.html 的完整實(shí)現(xiàn)并對(duì)照 package.json 了解依賴(lài)版本與構(gòu)建腳本即可在此基礎(chǔ)上替換為自己的數(shù)據(jù)集搭建瀏覽器端的通用高維數(shù)據(jù)探索工具。贊分享示例工程【免費(fèi)下載鏈接】tfjs-examplesExamples built with TensorFlow.js項(xiàng)目地址https://gitcode.com/gh_mirrors/tf/tfjs-examples點(diǎn)擊查看免費(fèi)下載相關(guān)推薦3步并行開(kāi)發(fā)Prime Agent Subagent 子智能體實(shí)戰(zhàn)指南3步并行開(kāi)發(fā)Prime Agent Subagent 子智能體實(shí)戰(zhàn)指南 認(rèn)證模塊要改、測(cè)試要補(bǔ)、審查任務(wù)還堆著主智能體只能一件件串行干。Prime Agen人工智能AI Agent代碼智能體工具調(diào)用交互助手MCP ClientsAgent 記憶2024年最受歡迎的流媒體代理工具Threadfin核心功能全解析2024年最受歡迎的流媒體代理工具Threadfin核心功能全解析 Threadfin 是一款基于 xTeVe 開(kāi)發(fā)的 M3U 代理工具專(zhuān)為 Kernel、后端音視頻Plotly 可視化 t-SNE 與 UMAP 降維投影從高維數(shù)據(jù)到 2D/3D 散點(diǎn)圖實(shí)戰(zhàn)基于 plotly.pyPlotly 可視化 t SNE 與 UMAP 降維投影從高維數(shù)據(jù)到 2D/3D 散點(diǎn)圖實(shí)戰(zhàn)基于 plotly.py 本篇技術(shù)指南以 doc/python數(shù)據(jù)可視化數(shù)據(jù)分析上一篇AlphaFold 3革命性蛋白質(zhì)結(jié)構(gòu)預(yù)測(cè)的7大關(guān)鍵技術(shù)突破與實(shí)戰(zhàn)指南下一篇gh_mirrors/de/developer組件設(shè)計(jì)構(gòu)建可復(fù)用UI創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考