blob: bfb8f0207861a0991ce16fbe44d00b6a7d9e8de6 [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">
<link href="https://fonts.googleapis.com/css?family=Lato|Roboto+Condensed" rel="stylesheet">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src = "https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
<script src = "https://cdn.datatables.net/fixedcolumns/3.2.3/js/dataTables.fixedColumns.min.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">
<script
src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
<script>
$('#firstable').hide();
$(document).ready(function() {
$("#xxxtabs").tabs( {
"show": function(event, ui) {
var oTable = $('div.dataTables_scrollBody>table.display', ui.panel).dataTable();
if ( oTable.length > 0 ) {
oTable.fnAdjustColumnSizing();
}
}
} );
$("input").bind("keydown", function(event) {
// track enter key
var keycode = (event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode));
if (keycode == 13) { // keycode for enter key
// force the 'Enter Key' to implicitly click the Update button
document.getElementById('SEARCH').click();
return false;
} else {
return true;
}});
$(".selector").tabs({ active: 1 });
$('#firsttable').DataTable({
"sScrollY": "780px",
"bScrollCollapse": true,
"bPaginate": false,
"bJQueryUI": true,
"dom": '<"top">rt<"bottom"flp><"clear">',
"aoColumnDefs": [
{ "sWidth": "10%", "aTargets": [ -1 ] }
]
} );
$('#secondtable').DataTable({
"sScrollY": "800px",
"bScrollCollapse": true,
"bPaginate": false,
"bJQueryUI": true,
"dom": '<"top">rt<"bottom"flp><"clear">',
"aoColumnDefs": [
{ "sWidth": "10%", "aTargets": [ -1 ] }
]
} );
});
$(function(){
$("#dropdownoptions").dialog({
title: "Options",
autoOpen: false,
modal: false,
draggable: false,
height: "auto",
width: "auto",
resizable: false,
buttons: {
"Cancel": function() {
$( this ).dialog( "close" );
},
"Apply": function() {
window.open($(location).attr('pathname')+'?'+$('form').serialize(), "_self");
}
}
});
});
$(function(){
$("#SEARCH").click(function() {
window.open($(location).attr('pathname')+'?'+$('form').serialize(), "_self");
});
});
$(function(){
$("#showoptions").click(function(){
$("#dropdownoptions").dialog("open");
var target = $(this);
$("#dropdownoptions").dialog("widget").position({
my: 'left bottom',
at: 'left bottom',
of: target
});
});
});
$( function() {
$( "#no_iterations" ).spinner({
spin: function( event, ui ) {
if ( ui.value < 1000 ) {
$( this ).spinner( "value", 1000 );
return false;
} else if ( ui.value > 10000 ) {
$( this ).spinner( "value", 10000 );
return false;
}
}
});
} );
$( function() {
$( "#neighbours" ).spinner({
spin: function( event, ui ) {
if ( ui.value < 0 ) {
$( this ).spinner( "value", 0 );
return false;
} else if ( ui.value > 200 ) {
$( this ).spinner( "value", 200 );
return false;
}
}
});
} );
$( function() {
$( "#cutoff" ).spinner({
spin: function( event, ui ) {
if ( ui.value < 100000 ) {
$( this ).spinner( "value", 100000 );
return false;
} else if ( ui.value > 2000000 ) {
$( this ).spinner( "value", 2000000 );
return false;
}
}
});
} );
$( function() {
$( "#tabs" ).tabs().addClass('tabs-min');
} );
$( function() {
$( ".controlgroup-vertical" ).controlgroup({
"direction": "vertical"
});
} );
$(function() {
$( document ).tooltip({
content: function() {
return $(this).attr('title');
}}
)
})
</script>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/derekovecs/js/tsne.js"></script>
<script src="/derekovecs/js/som.js"></script>
<script src="/derekovecs/js/labeler.js"></script>
<style>
body, input {
font-family: Lato, sans-serif;
font-size: 11pt;
}
h1, h2, h3 {
margin: 5px 10px 0 0;
color: rgb(246,168,0);
font-family: "Univers LT Std 47 Cn Lt", "Univers LT Std 67 Cn Lt", "Roboto Condensed", "Univers LT Std 67 Cn Bold", "UniversLTStd-BoldCn", "Times", 'League Gothic', Impact, sans-serif;
font-weight: bold;
line-height: 1.35;
letter-spacing: normal;
text-transform: uppercase;
text-shadow: none;
word-wrap: break-word;
showoptions, #SEARCH {
margin-left: 10px;
margin-right: 10px;
}
.tabs-left-vertical .ui-tabs-nav {
position: absolute;
width: 21em;
transform: translate(-100%,0%) rotate(-90deg);
transform-origin: 100% 0%;
}
.tabs-left-vertical .ui-tabs-nav li {
float: right;
}
.tabs-left-vertical .ui-tabs-panel {
padding-left: 3.5em;
}
.tabs-left-vertical .ui-tabs-panel {
height: 20em;
}
.mono {
font-family: "DejaVu Sans Mono", Inconsolata, SourceCodePro, Courier;
}
.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;
}
table.display {
width: 40% important!;
margin: 1; /* <- works for me this way ****/
}
table.dataTable thead th, table.dataTable thead td, table.dataTable tbody td {
padding: 2px 2px;
// border-bottom: 1px solid #111;
}
#collocators {
margin-bottom: 15px;
}
#topwrapper {
width: 100%;
// border: 1px solid red;
overflow: hidden; /* will contain if #first is longer than #second */
}
#wrapper {
// border: 1px solid red;
overflow: hidden; /* will contain if #first is longer than #second */
}
#header {
width: 100%;
// border: 1px solid red;
overflow: hidden; /* will contain if #first is longer than #second */
}
#pagetitle {
max-width: 460px;
margin-right: 20px;
float: left;
overflow: hidden; /* if you don't want #second to wrap below #first */
// border: 1px solid green;
}
#first {
margin-right: 20px;
float: left;
overflow: hidden; /* if you don't want #second to wrap below #first */
// border: 1px solid green;
}
#tabs {
margin-right: 20px;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
#options {
float: left;
width: 800px;
margin: 10px;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
#word {
width: 50%;
}
.tabs-min {
background: transparent;
border: none;
}
.tabs-min .ui-widget-header {
background: transparent;
border: none;
border-bottom: 1px solid #c0c0c0;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
.tabs-min .ui-tabs-nav .ui-state-default {
background: transparent;
border: none;
}
.tabs-min .ui-tabs-nav .ui-state-active {
background: transparent url(img/uiTabsArrow.png) no-repeat bottom center;
border: none;
}
.tabs-min .ui-tabs-nav .ui-state-default a {
color: #c0c0c0;
}
.tabs-min .ui-tabs-nav .ui-state-active a {
color: rgb(246,168,0);
}
#embed {
max-width: 802px;
border: 1px solid #333;
}
#second {
min-width: 800px;
// 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(true) { // (<%= $show_som %>) {
makeSOM(j, <%= $no_iterations %>);
}
}
var queryword;
function onload() {
queryword = document.getElementById('word');
}
function queryKorAP() {
window.open('http://korap.ids-mannheim.de/kalamar/?q='+queryword.value, 'KorAP');
}
function queryKorAPCII(query) {
window.open('http://korap.ids-mannheim.de/kalamar/?ql=cosmas2&q='+query, 'KorAP');
}
</script>
</head>
<body onload="onload()">
<div id="header">
<div id="pagetitle">
<h1>DeReKo-Vectors</h1>
</div>
<div id="options" class="widget">
<form id="queryform">
<input id="word" type="text" name="word" placeholder="Word(s) to be searched" 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."/>
<input id="SEARCH" type="button" value="SEARCH">
<input type="button" id="showoptions" name="showoptions" value="Options" />
</form>
<div id="dropdownoptions" style="display: hidden">
<form id="optionsform">
<div class="controlgroup-vertical">
<label for="cutoff">cut-off</label>
<input id="cutoff" type="text" name="cutoff" size="10" value="<%= $cutoff %>" title="Only consider the most frequent x word forms.">
<label for="dedupe">dedupe</label>
<input id="dedupe" type="checkbox" name="dedupe" value="1" <%= ($dedupe ? "checked" : "") %> title="radically filter out any near-duplicates">
% if($mergedEnd > 0) {
<label for="sbf">backw.</label>
<input id="sbf" type="checkbox" name="sbf" value="1" <%= ($searchBaseVocabFirst ? "checked" : "") %> title="If checkecked base vocabulary will be searched first. Otherwise merged vocabulray will be searched first.">
% }
<label for="neighbours">max. neighbours:</label>
<input id="neighbours" size="4" name="n" value="<%= $no_nbs %>">
<label for="no_iterations">max. iterations</label>
<input id="no_iterations" name="N" size="4" value="<%= $no_iterations %>">
<!-- <label for="dosom">SOM</label>
<input id="dosom" type="checkbox" name="som" value="1" <%= ($show_som ? "checked" : "") %>> -->
% if($collocators) {
<label for="sortby">window/sort</label>
<select id="sortby" name="sort">
<option value="0" <%= ($sort!=1 && $sort!=2? "selected":"") %>>auto focus</option>
<option value="1" <%= ($sort==1? "selected":"") %>>any single position</option>
<option value="2" <%= ($sort==2? "selected":"") %>>whole window</option>
</select>
% }
<input type="button" value="→ KorAP" onclick="queryKorAP();" title="query word with KorAP"/>
</div>
</form>
</div>
</div>
</div>
<div id="topwrapper">
<div id="tabs">
<ul>
<li><a href="#tabs-1">Semantics (TSNE-map)</a></li>
<li><a href="#tabs-2">Semantics (SOM)</a></li>
<li><a href="#tabs-3">Syntagmatic (collocators)</a></li>
</ul>
<div id="tabs-1">
% if($lists && (@$lists) > 0 && (@$lists)[0]) {
<div id="wrapper">
<div id="first" style="width:220px">
<table class="display compact nowrap" id="firsttable">
<thead>
<tr>
<th align="right">#</th><th align="right">cos</th><th align="left">paradigmatic</th>
</tr>
</thead>
<tbody>
% 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"/>
% }
</tr>
% last if($i >= 100);
% }
% }
</tbody>
</table>
</div>
<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>
% } else {
<div id="wrapper">
<p>
ERROR: "<%= $word %>" not found in vocabluary.
</p>
</div>
% }
<div id="second">
<div id="embed">
</div>
<div id="cost">
</div>
</div>
</div>
</div>
<div id="tabs-2">
<div id="som2" style="width: 800; height: 800px">
</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>
<div id="tabs-3">
<div id="second" style="width:500px">
<table class="display compact nowrap" id="secondtable">
<thead>
<tr>
% if($collocators) {
<th>#</th>
<th align="right" title="The window around the target word that is considered for summation.">w'</th>
<th align="right" title="Raw (max.) activation of the collocator in the output layers.">a</th>
<th title="Σp(c<sub><small>@</small></sub>) – Sum of the probability approximations that the combination of the target word and the collocator at the relative position @ come from the training corpus. Single approximations can be distorted because of sub-sampling frequent words and the sum cannot itself be interpreted as probability." align="right">Σp</th>
<th align="right">Σp/|w|</th>
<th title="c" align="left">collocator</th>
% }
</tr>
</thead>
<tbody>
% for(my $i=0; $i < 100; $i++) {
% my $c = ($collocators? (@$collocators)[$i] : 0);
<tr>
<td align="right">
<%= $i %>
</td>
% if($c) {
<td align="right">
<span class="mono"><%= bitvec2window( $c->{pos} ) %></span>
</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 onclick="<%= sprintf("queryKorAPCII('%s /w5 %s')", $c->{word}, $word) =%>"
title="freq. rank: <%= $c->{rank} =%>">
<%= $c->{word} %>
</a>
</td>
% } else {
<td colspan="5"/>
% }
</tr>
% }
</tbody>
</table>
</div> <!-- - tab2 -->
</div> <!-- tabs -->
</div>
</div> <!-- topwrapper -->
<div style="clear: both;"></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>