blob: 3c2ca3de5d51609680d57046859d75939a9cfc29 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>DeReKo-Word-Vector-Distances: <%= $word %></title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script
src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
<script>
$(function() {
$( document ).tooltip({
content: function() {
return $(this).attr('title');
}}
)
})
</script>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/word2vec/js/tsne.js"></script>
<script src="/word2vec/js/som.js"></script>
<script src="/word2vec/js/labeler.js"></script>
<style>
body, input {
font-family: Arial, sans-serif;
font-size: 11pt;
}
.ui-tooltip-content {
font-size: 9pt;
color: #222222;
}
svg > .ui-tooltip-content {
font-size: 8pt;
color: #222222;
}
a.merged {
color: green;
fill: green;
}
#first a {
text-decoration: none;
}
a.marked, #first a.marked {
text-decoration: underline;
}
a.target {
color: red;
fill: red;
}
#collocators {
margin-bottom: 15px;
}
#wrapper {
width: 100%;
// border: 1px solid red;
overflow: hidden; /* will contain if #first is longer than #second */
}
#first {
margin-right: 20px;
float: left;
// border: 1px solid green;
}
#second {
border: 1px solid #333;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
#som2 svg {
border: 1px solid #333;
}
#cost {
font-size: 8pt;
color: #222222;
margin-top: 4px;
margin-bottom: 12px;
}
#sominfo1, #sominfo {
font-size: 8pt;
color: #222222;
margin-top: 0px;
}
#somcolor1, #somcolor2, #somcolor3 {
display: inline-block;
height: 10px;
width: 10px;
}
#third {
border: 1px solid #333;
}
</style>
<script>
var opt = {epsilon: <%= $epsilon %>, perplexity: <%= $perplexity %>},
mapWidth = 800, // width map
mapHeight = 800,
jitterRadius = 7;
var T = new tsnejs.tSNE(opt); // create a tSNE instance
var Y;
var data;
var labeler;
function applyJitter() {
svg.selectAll('.tsnet')
.data(labels)
.transition()
.duration(50)
.attr("transform", function(d, i) {
T.Y[i][0] = (d.x - mapWidth/2 - tx)/ss/20;
T.Y[i][1] = (d.y - mapHeight/2 - ty)/ss/20;
return "translate(" +
(d.x) + "," +
(d.y) + ")";
});
}
function updateEmbedding() {
var Y = T.getSolution();
svg.selectAll('.tsnet')
.data(data.words)
.attr("transform", function(d, i) {
return "translate(" +
((Y[i][0]*20*ss + tx) + mapWidth/2) + "," +
((Y[i][1]*20*ss + ty) + mapHeight/2) + ")"; });
}
var svg;
var labels = [];
var anchor_array = [];
var text;
function drawEmbedding() {
$("#embed").empty();
var div = d3.select("#embed");
// get min and max in each column of Y
var Y = T.Y;
svg = div.append("svg") // svg is global
.attr("width", mapWidth)
.attr("height", mapHeight);
var g = svg.selectAll(".b")
.data(data.words)
.enter().append("g")
.attr("class", "tsnet");
g.append("a")
.attr("xlink:href", function(word) {
return (data.urlprefix+word);})
.attr("class", function(d, i) {
var res="";
if(data.marked[i]) {
res="marked ";
}
if(data.target.indexOf(" "+d+" ") >= 0) {
return res+"target";
} else if(data.ranks[i] < data.mergedEnd) {
return res+"merged";
} else {
return res;
}
})
.attr("title", function(d, i) {
if(data.mergedEnd > 0) {
if(data.ranks[i] >= data.mergedEnd) {
return "rank: "+i +" "+"freq. rank: "+(data.ranks[i]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return "rank: "+i +" "+"freq. rank: "+data.ranks[i].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " (merged vocab)";
}
} else {
return "rank: "+i +" "+"freq. rank: "+data.ranks[i].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
})
.append("text")
.attr("text-anchor", "top")
.attr("font-size", 12)
.text(function(d) { return d; });
var zoomListener = d3.behavior.zoom()
.scaleExtent([0.1, 10])
.center([0,0])
.on("zoom", zoomHandler);
zoomListener(svg);
}
var tx=0, ty=0;
var ss=1;
var iter_id=-1;
function zoomHandler() {
tx = d3.event.translate[0];
ty = d3.event.translate[1];
ss = d3.event.scale;
updateEmbedding();
}
var stepnum = 0;
function stopStep() {
clearInterval(iter_id);
text = svg.selectAll("text");
// jitter function needs different data and co-ordinate representation
labels = d3.range(data.words.length).map(function(i) {
var x = (T.Y[i][0]*20*ss + tx) + mapWidth/2;
var y = (T.Y[i][1]*20*ss + ty) + mapHeight/2;
anchor_array.push({x: x, y: y, r: jitterRadius});
return {
x: x,
y: y,
name: data.words[i]
};
});
// get the actual label bounding boxes for the jitter function
var index = 0;
text.each(function() {
labels[index].width = this.getBBox().width;
labels[index].height = this.getBBox().height;
index += 1;
});
// setTimeout(updateEmbedding, 1);
// setTimeout(
labeler = d3.labeler()
.label(labels)
.anchor(anchor_array)
.width(mapWidth)
.height(mapHeight)
.update(applyJitter);
// .start(1000);
iter_id = setInterval(jitterStep, 1);
}
var jitter_i=0;
function jitterStep() {
if(jitter_i++ > 100) {
clearInterval(iter_id);
} else {
labeler.start2(10);
applyJitter();
}
}
var last_cost=1000;
function step() {
var i = T.iter;
if(i > <%= $no_iterations %>) {
stopStep();
} else {
var cost = Math.round(T.step() * 100000) / 100000; // do a few steps
$("#cost").html("tsne iteration " + i + ", cost: " + cost.toFixed(5));
if(i % 250 == 0 && cost >= last_cost) {
stopStep();
} else {
last_cost = cost;
updateEmbedding();
}
}
}
function showMap(j) {
data=j;
T.iter=0;
T.initDataRaw(data.vecs); // init embedding
drawEmbedding(); // draw initial embedding
if(iter_id >= 0) {
clearInterval(iter_id);
}
//T.debugGrad();
iter_id = setInterval(step, 1);
if(<%= $show_som %>) {
makeSOM(j, <%= $no_iterations %>);
}
}
</script>
</head>
<body>
<form method="GET">
word(s):
<input type="text" name="word" size="20" value="<%= $word %>" title="When looking for multiple words use spaces as separators to search around the average vector and | as separator to get the neighbours for each word.">
% if($mergedEnd > 0) {
backw. <input type="checkbox" name="sbf" value="1" <%= ($searchBaseVocabFirst ? "checked" : "") %> title="If checkecked base vocabulary will be searched first. Otherwise merged vocabulray will be searched first.">
% }
max. neighbours: <input type="text" size="8" name="n" value="<%= $no_nbs %>">
max. iterations: <input type="text" name="N" size="8" value="<%= $no_iterations %>">
SOM <input type="checkbox" name="som" value="1" <%= ($show_som ? "checked" : "") %>>
% if($collocators) {
<span> </span>sort collocators by
<select name="sort">
<option value="0" <%= ($sort!=1? "selected":"") %>>responsiveness</option>
<option value="1" <%= ($sort==1? "selected":"") %>>mean p</option>
</select>
% }
<span> </span><input type="submit" value="Show">
</form>
<br>
% if($lists) {
<div id="wrapper">
<table id="first">
<tr>
<th align="right">#</th><th align="right">cos</th><th align="left">paradigmatic</th>
% if($collocators) {
<th title="Position in winodw around target word. Absolute value can be too low because of sub-sampling frequent words.">@</th><th align="right" title="&#34;Responsivenes&#34; of the collocator at the relative position @. Approximation of the probability that the combination of the target word and the collocator at the relative position @ come from the corpus.">resp.</th><th title="Probability of the collocator at window location @."align="right">p(c<sub><small>@</small></sub>)</th><th align="right">Σp(c<sub><small>@</small></sub>)/|w|</th><th align="left">syntagmatic</th>
% }
</tr>
% my $j=0; my @words; my @vecs; my @ranks; my @marked;
% for my $list (@$lists) {
% my $i=0; while($list) {
% my $item = (@$list)[$i];
% my $c = ($collocators? (@$collocators)[$i] : 0);
% last if(!$c && !$item);
<tr>
<td align="right">
<%= ++$i %>.
</td>
% if($item) {
% if(!grep{$_ eq $item->{word}} @words) {
% push @vecs, $item->{vector};
% push @words, $item->{word};
% push @ranks, $item->{rank};
% push @marked, ($marked->{$item->{word}}? 1 : 0);
% }
<td align="right">
<%= sprintf("%.3f", $item->{dist}) %>
</td>
<td>
% my $class = ($marked->{$item->{word}}? "marked " : "");
% my $r = $item->{rank};
% if($r < $mergedEnd) {
% $class .= "merged";
% $r .= " (merged vocab)";
% } elsif($mergedEnd!=0 && $r > $mergedEnd) {
% $r -= $mergedEnd;
% }
<a class="<%= $class =%>"
title="freq. rank: <%= $r =%>"
href="<%= url_with->query([word => $item->{word}]) =%>">
<%= $item->{word} =%>
</a>
</td>
% } else {
<td colspan="2"/>
% }
% if($c) {
<td align="right">
<%= $c->{pos} %>:
</td>
<td align="right">
<%= sprintf("%.3f", $c->{dist}) %>
</td>
<td align="right">
<%= sprintf("%.3e", $c->{norm}) %>
</td>
<td align="right">
<%= sprintf("%.3e", $c->{sum}) %>
</td>
<td align="left">
<a href="<%= url_with->query([word => $c->{word}]) =%>">
<%= $c->{word} %>
</td>
% } else {
<td colspan="5"/>
% }
</tr>
% }
% }
</table>
<script>
% use Mojo::ByteStream 'b';
% my $urlprefix = url_with->query([word=>'']);
$(window).load(function() {
showMap(<%= b(Mojo::JSON::to_json({target => " $word ", mergedEnd=> $mergedEnd, words => \@words, vecs => \@vecs, ranks => \@ranks, marked => \@marked, urlprefix => $urlprefix})); %>);
});
</script>
% }
<div id="second" style="width:800px; height:800px; font-family: arial;">
<div id="embed">
</div>
</div>
<div id="cost"></div>
% if($show_som) {
<div id="som2">
</div>
<div id="sominfo1"><span id="somcolor1"> </span> <span id="somword1"> </span> <span id="somcolor2"> </span> <span id="somword2"> </span> <span id="somcolor3"> </span></div>
<div id="sominfo">SOM iteration <span id="iterations">0</span></div>
% }
</div>
% if($training_args) {
<p>
Word vector model trained with <a href="https://code.google.com/p/word2vec/">word2vec</a> using the following parameters: <pre><%= $training_args %></pre>
</p>
% }
</body>
</html>