blob: 6b5c4b82c5ec9ffa815436390525e1fa84b4b602 [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>
<script src = "https://cdn.datatables.net/plug-ins/1.10.16/sorting/scientific.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>
var urlParams = new URLSearchParams(window.location.search);
$('#firstable').hide();
$(document).ready(function() {
$("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 ] }
]
} );
function bitvec2window(n) {
var str = n.toString(2).padStart(10, "0")
.replace(/^([0-9]{5})/, '$1x')
.replace(/0/g, '·')
.replace(/1/g, '+');
return str;
}
var collocatorData = <%= b(Mojo::JSON::to_json($collocators)) %>;
if (collocatorData != null) {
var t = $('#secondtable').DataTable({
data: collocatorData,
"sScrollY": "800px",
"bScrollCollapse": true,
"bPaginate": false,
"bJQueryUI": true,
"dom": '<"top">rt<"bottom"flp><"clear">',
"columns": [
{ "data": "rank", type: "allnumeric" },
{ "data": "pos", width: "7%", sClass: "dt-center mono compact", render: function ( data, type, row ) {return bitvec2window(data) }},
{ "data": "max", render: function ( data, type, row ) {return data.toFixed(3) }},
{ "data": "conorm", render: function ( data, type, row ) {return data.toFixed(3) }},
{ "data": "prob", type: "scientific", render: function ( data, type, row ) {return data.toExponential(3) } },
{ "data": "cprob", type: "scientific", render: function ( data, type, row ) {return data.toExponential(3) } },
{ "data": "overall", type: "scientific", render: function ( data, type, row ) {return data.toExponential(3) } },
{ "data": "word", sClass: "collocator" }
],
"columnDefs": [
{ className: "dt-right", "targets": [0,2,3,4,5,6] },
{ className: "dt-center", "targets": [ 1] },
{ "searchable": false,
"orderable": false,
"targets": 0
},
{ "type": "scientific", targets: [2,3,4,5,6] },
{ "orderSequence": [ "desc" ], "targets": [ 2, 3, 4, 5, 6 ] },
{ "orderSequence": [ "asc", "desc" ], "targets": [ 1, 7 ] },
],
"order": [[ 4, 'desc' ]],
} );
t.on( 'order.dt search.dt', function () {
t.column(0, {order:'applied'}).nodes().each( function (cell, i) {
cell.innerHTML = i+1;
} );
} ).draw();
}
$("#tabs").css("visibility", "visible"); // now we can show the tabs
});
$(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(){
$("td.collocator").click(function(){
queryKorAPCII(this.textContent + " /w5 " + urlParams.get('word'));
});
});
$(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;
}
}
});
} );
var tabactivated = {}
$( 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;
}
#header {
width: 100%;
// border: 1px solid red;
overflow: hidden; /* will contain if #first is longer than #second */
}
#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 */
}
#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;
}
#options {
float: left;
width: 800px;
margin: 10px;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
#word {
width: 50%;
}
#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 */
}
.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: none">
<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 style="visibility: hidden;" 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() {
var vecs = <%= b(Mojo::JSON::to_json($lists->[0])) %>;
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">
<div id="not-found-dialog" title="Not found">
<p>ERROR: "<%= $word %>" not found in vocabluary.</p>
<p>If you are sure you have spelled the word as intended, you can try to increase the cutoff parameter in the options menu.</p>
</div>
<script>
$( function() {
$( "#not-found-dialog" ).dialog({
autoOpen: true,
modal: true,
draggable: false,
height: "auto",
width: "auto",
resizable: false,
buttons: {
"OK": function() {
$( this ).dialog( "close" );
},
"Apply": function() {
window.open($(location).attr('pathname')+'?'+$('form').serialize(), "_self");
}
}
});
});
</script>
</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 style="width: 800px" id="secondt">
<table class="display compact nowrap" id="secondtable">
<thead>
<tr>
% if($collocators) {
<th>#</th>
<th align="center" title="The columns (c) around the target are considered for summation are marked with *.">w'</th>
<th align="right" title="Raw (max.) activation of the collocator in the output layers.">max(a)</th>
<th title="Co-normalized raw activation sum of the collocator in the selected columns." align="right">⊥Σa</th>
<th title="Sum of activations over the selected colunns normalized by the total activation sum of the selected columns." align="right">Σa/Σc</th>
<th title="Sum of the column normalized activations over the selected colunns." align="right">Σ(a/c)</th>
<th title="Sum of the activations over the whole window normalized by the total window sum (no auto-focus)." align="right">Σa/Σw</th>
<th align="left">collocator</th>
% }
</tr>
</thead>
<tbody>
<tr>
<td align="right">
</td>
<td align="right">
</td>
<td align="right">
</td>
<td align="right">
</td>
<td align="right">
</td>
<td align="right">
</td>
<td align="right">
</td>
<td align="left">
</td>
</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>