未验证 提交 6408cbfe 编写于 作者: J Jeff Wang 提交者: GitHub

Embedding visualization connect (#356)

* Make the frontend to fetch the data from the flask server.

* Provide loading animation.

* Fixed format style
Fix the issue where the display label not working.

* Provide a 2D 3D switcher.
Display the chart in 3D.

* Include PCA method to do dimension reduction

* Fix the style.

* Fix typo
Use === to compare type as well.
上级 5a077745
......@@ -6,7 +6,8 @@
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
module.exports = function(path, queryParam, postParam) {
if (queryParam.dimension === '3') {
return {
// moock delay
_timeout: 0,
......@@ -18,19 +19,73 @@ module.exports = function (path, queryParam, postParam) {
msg: 'SUCCESS',
data: {
"embedding": [
[10.0, 8.04, "yellow"],
[8.0, 6.95, "blue"],
[13.0, 7.58, "red"],
[9.0, 8.81, "king"],
[11.0, 8.33, "queen"],
[14.0, 9.96, "man"],
[6.0, 7.24, "women"],
[4.0, 4.26, "kid"],
[12.0, 10.84, "adult"],
[7.0, 4.82, "light"],
[5.0, 5.68, "dark"]
[10.0, 8.04, 3],
[8.0, 6.95, 4],
[13.0, 7.58, 1],
[9.0, 8.81, 3],
[11.0, 8.33, 5],
[14.0, 9.96, 6],
[6.0, 7.24, 1],
[4.0, 4.26, 2],
[12.0, 10.84, 6],
[7.0, 4.8, 3],
[5.0, 5.68, 3]
],
"labels": [
"yellow",
"blue",
"red",
"king",
"queen",
"man",
"women",
"kid",
"adult",
"light",
"dark"
]
}
}
};
} else {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: {
"embedding": [
[10.0, 8.04],
[8.0, 6.95],
[13.0, 7.58],
[9.0, 8.81],
[11.0, 8.33],
[14.0, 9.96],
[6.0, 7.24],
[4.0, 4.26],
[12.0, 10.84],
[7.0, 4.8],
[5.0, 5.68]
],
"labels": [
"yellow",
"blue",
"red",
"king",
"queen",
"man",
"women",
"kid",
"adult",
"light",
"dark"
]
}
}
};
}
};
......@@ -20,7 +20,8 @@
"d3-format": "^1.2.1",
"dagre": "^0.8.2",
"dagre-d3": "^0.6.1",
"echarts": "^3.8.5",
"echarts": "^4.0.0",
"echarts-gl": "^1.1.0",
"file-saver": "^1.3.3",
"graphlib": "^1.0.5",
"htmlcs": "^0.4.1",
......
......@@ -5,6 +5,7 @@
:config="config"
:displayWordLabel="config.displayWordLabel"
:searchText="config.searchText"
:dimension="config.dimension"
:embedding_data="embedding_data"
></ui-chart>
</div>
......@@ -35,20 +36,45 @@ export default {
config: {
searchText: '',
displayWordLabel: true,
dimension: "2",
reduction: "tsne",
running: true
},
embedding_data: []
}
},
created() {
getHighDimensionalDatasets().then(({errno, data}) => {
this.embedding_data = data.embedding;
});
this.fetchDatasets()
},
watch: {
'config.dimension': function(val) {
this.fetchDatasets()
},
'config.reduction': function(val) {
this.fetchDatasets()
}
},
mounted() {
autoAdjustHeight();
},
methods: {
fetchDatasets() {
// Fetch the data from the server. Passing dimension and reduction method
let params = {
dimension: this.config.dimension,
reduction: this.config.reduction
};
getHighDimensionalDatasets(params).then(({errno, data}) => {
var vector_data = data.embedding;
var labels = data.labels;
for ( var i = 0; i < vector_data.length; i ++) {
vector_data[i].push(labels[i])
}
this.embedding_data = vector_data
});
},
}
};
......
......@@ -7,9 +7,10 @@
<script>
import echarts from 'echarts';
import 'echarts-gl';
export default {
props: ['config', 'displayWordLabel', 'searchText', 'embedding_data'],
props: ['config', 'displayWordLabel', 'searchText', 'embedding_data', 'dimension'],
data() {
return {
width: 900,
......@@ -18,20 +19,21 @@ export default {
},
computed: {
computedStyle() {
return 'height:' + this.height + 'px;'
+ 'width:' + this.width + 'px;';
return 'height:' + this.height + 'px;' +
'width:' + this.width + 'px;';
}
},
created() {
},
created() {},
mounted() {
this.createChart();
this.setChartsOptions();
this.myChart.showLoading()
this.set2DChartOptions();
this.setDisplayWordLabel();
},
watch: {
embedding_data: function(val) {
this.myChart.hideLoading();
this.myChart.setOption({
series: [{
// Grab the 'matched' series data
......@@ -43,13 +45,25 @@ export default {
displayWordLabel: function(val) {
this.setDisplayWordLabel()
},
dimension: function(val) {
this.myChart.clear()
this.myChart.showLoading()
if (val === "2") {
this.set2DChartOptions();
this.setDisplayWordLabel();
} else {
this.set3DChartOptions();
this.setDisplayWordLabel();
}
},
searchText: function(val) {
// Filter the data that has the hasPrefix
var matched_words = []
if (val != '') {
val = val.toLowerCase()
function hasPrefix(value) {
var word = value[2]
var word = value[value.length - 1]
return (typeof word == "string" && word.toLowerCase().startsWith(val))
}
......@@ -72,13 +86,12 @@ export default {
let el = this.$refs.chartBox;
this.myChart = echarts.init(el);
},
setChartsOptions(){
set2DChartOptions() {
var typeD = "normal";
var option = {
xAxis: {},
yAxis: {},
series: [
{
series: [{
name: "all",
symbolSize: 10,
data: this.embedding_data,
......@@ -87,7 +100,7 @@ export default {
{
name: "matched",
animation: false,
symbolSize:10,
symbolSize: 10,
data: [],
itemStyle: {
normal: {
......@@ -97,8 +110,8 @@ export default {
label: {
normal: {
show: true,
formatter: function (param) {
return param.data[2];
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top'
}
......@@ -109,13 +122,56 @@ export default {
};
this.myChart.setOption(option);
},
set3DChartOptions() {
var symbolSize = 2.5;
var option3d = {
grid3D: {},
xAxis3D: {
type: 'category'
},
yAxis3D: {},
xAxis3D: {},
zAxis3D: {},
dataset: {
source: this.embedding_data
},
series: [
{
name: "all",
type: 'scatter3D',
symbolSize: symbolSize,
data: []
},
{
name: "matched",
animation: false,
symbolSize: symbolSize,
data: [],
label: {
normal: {
show: true,
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top'
}
},
type: 'scatter3D'
}
]
}
this.myChart.setOption(option3d);
},
setDisplayWordLabel() {
this.myChart.setOption({
series: [{
// Grab the 'all' series data
name: 'all',
label: {
normal: {
show: this.displayWordLabel,
formatter: function (param) {
return param.data[2];
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top'
},
......@@ -123,9 +179,9 @@ export default {
show: true,
}
}
})
}]
});
},
}
};
......@@ -136,7 +192,4 @@ export default {
float left
padding 10px
position relative
</style>
......@@ -13,6 +13,16 @@
label="Display All Labels"
v-model="config.displayWordLabel" dark></v-checkbox>
<v-radio-group label="Dimension" v-model="config.dimension" dark>
<v-radio label="2D" value="2"></v-radio>
<v-radio label="3D" value="3"></v-radio>
</v-radio-group>
<v-radio-group label="Reduction Method" v-model="config.reduction" dark>
<v-radio label="T-SNE" value="tsne"></v-radio>
<v-radio label="PCA" value="pca"></v-radio>
</v-radio-group>
<v-btn :color="config.running ? 'primary' : 'error'"
v-model="config.running"
@click="toggleAllRuns"
......@@ -64,5 +74,3 @@ export default {
margin-bottom 10px
</style>
......@@ -196,6 +196,32 @@ def get_texts(storage, mode, tag, num_records=100):
return res
def get_embeddings(storage,
mode,
tag,
reduction,
dimension=2,
num_records=5000):
with storage.mode(mode) as reader:
embedding = reader.embedding(tag)
labels = embedding.get_all_labels()
high_dimensional_vectors = embedding.get_all_embeddings()
# TODO: Move away from sklearn
if reduction == 'tsne':
from sklearn.manifold import TSNE
tsne = TSNE(
perplexity=30, n_components=dimension, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(high_dimensional_vectors)
elif reduction == 'pca':
from sklearn.decomposition import PCA
pca = PCA(n_components=3)
low_dim_embs = pca.fit_transform(high_dimensional_vectors)
return {"embedding": low_dim_embs.tolist(), "labels": labels}
def get_histogram(storage, mode, tag):
with storage.mode(mode) as reader:
histogram = reader.histogram(tag)
......
......@@ -236,6 +236,14 @@ def texts():
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/data/plugin/embeddings/embeddings')
def embeddings():
dimension = request.args.get('dimension')
reduction = request.args.get('reduction')
key = os.path.join('/data/plugin/embeddings/embeddings', dimension, reduction)
data = cache_get(key, try_call, lib.get_embeddings, log_reader, 'train', 'scratch/embedding', reduction, int(dimension))
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/data/plugin/graphs/graph')
def graph():
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册