blob: 719cb5ae72f112144d70ec83cd4d8e547c169ac6 [file] [log] [blame]
Marc Kupietz83305222016-04-28 09:57:22 +02001<!DOCTYPE html>
2<html>
3 <head>
4 <title>DeReKo-Word-Vector-Distances: <%= $word %></title>
Marc Kupietz80bd7b92017-07-04 16:25:54 +02005 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
6 <script src="http://code.jquery.com/jquery-latest.min.js"></script>
Marc Kupietz694610d2017-11-25 18:30:03 +01007 <script src = "https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
8 <script src = "https://cdn.datatables.net/fixedcolumns/3.2.3/js/dataTables.fixedColumns.min.js"></script>
9 <link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">
Marc Kupietz80bd7b92017-07-04 16:25:54 +020010 <script
11 src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
12 integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
13 crossorigin="anonymous"></script>
14 <script>
Marc Kupietz694610d2017-11-25 18:30:03 +010015$(document).ready(function() {
16 $("#tabs").tabs( {
17 "show": function(event, ui) {
18 var oTable = $('div.dataTables_scrollBody>table.display', ui.panel).dataTable();
19 if ( oTable.length > 0 ) {
20 oTable.fnAdjustColumnSizing();
21 }
22 }
23 } );
24
25 $('#firsttable').DataTable({
26 "sScrollY": "800px",
27 "bScrollCollapse": true,
28 "bPaginate": false,
29 "bJQueryUI": true,
30 "dom": '<"top">rt<"bottom"flp><"clear">',
31 "aoColumnDefs": [
32 { "sWidth": "10%", "aTargets": [ -1 ] }
33 ]
34 } );
35});
Marc Kupietz83305222016-04-28 09:57:22 +020036 $(function() {
37 $( document ).tooltip({
38 content: function() {
39 return $(this).attr('title');
40 }}
41 )
42 })
Marc Kupietz694610d2017-11-25 18:30:03 +010043
Marc Kupietz83305222016-04-28 09:57:22 +020044 </script>
45 <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
Marc Kupietz554aff52017-11-09 14:42:09 +010046 <script src="/derekovecs/js/tsne.js"></script>
47 <script src="/derekovecs/js/som.js"></script>
48 <script src="/derekovecs/js/labeler.js"></script>
Marc Kupietz83305222016-04-28 09:57:22 +020049 <style>
50 body, input {
51 font-family: Arial, sans-serif;
52 font-size: 11pt;
53 }
Marc Kupietz30ca4342017-11-22 21:21:20 +010054
55 .mono {
56 font-family: "DejaVu Sans Mono", Inconsolata, SourceCodePro, Courier;
57 }
58
Marc Kupietz83305222016-04-28 09:57:22 +020059 .ui-tooltip-content {
60 font-size: 9pt;
61 color: #222222;
62 }
63
64 svg > .ui-tooltip-content {
65 font-size: 8pt;
66 color: #222222;
67 }
68
69 a.merged {
70 color: green;
71 fill: green;
72 }
73
74 #first a {
75 text-decoration: none;
76 }
77
78 a.marked, #first a.marked {
79 text-decoration: underline;
80 }
Marc Kupietzf4b49392016-04-28 10:49:56 +020081
Marc Kupietz83305222016-04-28 09:57:22 +020082 a.target {
83 color: red;
84 fill: red;
85 }
Marc Kupietz694610d2017-11-25 18:30:03 +010086
87table.display {
88 width: 40% important!;
89 margin: 0; /* <- works for me this way ****/
90}
91table.dataTable thead th, table.dataTable thead td, table.dataTable tbody td {
92 padding: 2px 2px;
93// border-bottom: 1px solid #111;
94}
Marc Kupietz83305222016-04-28 09:57:22 +020095 #collocators {
96 margin-bottom: 15px;
97 }
98
99 #wrapper {
100 width: 100%;
101 // border: 1px solid red;
102 overflow: hidden; /* will contain if #first is longer than #second */
103 }
104 #first {
105 margin-right: 20px;
106 float: left;
107 // border: 1px solid green;
108 }
109 #second {
110 border: 1px solid #333;
111 overflow: hidden; /* if you don't want #second to wrap below #first */
112 }
113 #som2 svg {
114 border: 1px solid #333;
115 }
116
117 #cost {
118 font-size: 8pt;
119 color: #222222;
120 margin-top: 4px;
121 margin-bottom: 12px;
122 }
123
124 #sominfo1, #sominfo {
125 font-size: 8pt;
126 color: #222222;
127 margin-top: 0px;
128 }
129
130 #somcolor1, #somcolor2, #somcolor3 {
131 display: inline-block;
132 height: 10px;
133 width: 10px;
134 }
135
136 #third {
137 border: 1px solid #333;
138 }
139
140 </style>
141 <script>
142
143 var opt = {epsilon: <%= $epsilon %>, perplexity: <%= $perplexity %>},
144 mapWidth = 800, // width map
145 mapHeight = 800,
146 jitterRadius = 7;
147
148 var T = new tsnejs.tSNE(opt); // create a tSNE instance
149
150 var Y;
151
152 var data;
153 var labeler;
154
155 function applyJitter() {
156 svg.selectAll('.tsnet')
157 .data(labels)
158 .transition()
159 .duration(50)
160 .attr("transform", function(d, i) {
161 T.Y[i][0] = (d.x - mapWidth/2 - tx)/ss/20;
162 T.Y[i][1] = (d.y - mapHeight/2 - ty)/ss/20;
163 return "translate(" +
164 (d.x) + "," +
165 (d.y) + ")";
166 });
167 }
168
169 function updateEmbedding() {
170 var Y = T.getSolution();
171 svg.selectAll('.tsnet')
172 .data(data.words)
173 .attr("transform", function(d, i) {
174 return "translate(" +
175 ((Y[i][0]*20*ss + tx) + mapWidth/2) + "," +
176 ((Y[i][1]*20*ss + ty) + mapHeight/2) + ")"; });
177 }
178
179 var svg;
180 var labels = [];
181 var anchor_array = [];
182 var text;
183
184 function drawEmbedding() {
185 $("#embed").empty();
186 var div = d3.select("#embed");
187
188 // get min and max in each column of Y
189 var Y = T.Y;
190
191 svg = div.append("svg") // svg is global
192 .attr("width", mapWidth)
193 .attr("height", mapHeight);
194
195 var g = svg.selectAll(".b")
196 .data(data.words)
197 .enter().append("g")
198 .attr("class", "tsnet");
199
200 g.append("a")
Marc Kupietzf4b49392016-04-28 10:49:56 +0200201 .attr("xlink:href", function(word) {
202 return (data.urlprefix+word);})
Marc Kupietz83305222016-04-28 09:57:22 +0200203 .attr("class", function(d, i) {
204 var res="";
205 if(data.marked[i]) {
206 res="marked ";
207 }
208 if(data.target.indexOf(" "+d+" ") >= 0) {
209 return res+"target";
210 } else if(data.ranks[i] < data.mergedEnd) {
211 return res+"merged";
212 } else {
213 return res;
214 }
215 })
216 .attr("title", function(d, i) {
217 if(data.mergedEnd > 0) {
218 if(data.ranks[i] >= data.mergedEnd) {
219 return "rank: "+i +" "+"freq. rank: "+(data.ranks[i]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
220 } else {
221 return "rank: "+i +" "+"freq. rank: "+data.ranks[i].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " (merged vocab)";
222 }
223 } else {
224 return "rank: "+i +" "+"freq. rank: "+data.ranks[i].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
225 }
226 })
227 .append("text")
228 .attr("text-anchor", "top")
229 .attr("font-size", 12)
230 .text(function(d) { return d; });
231
232 var zoomListener = d3.behavior.zoom()
233 .scaleExtent([0.1, 10])
234 .center([0,0])
235 .on("zoom", zoomHandler);
236 zoomListener(svg);
237 }
238
239 var tx=0, ty=0;
240 var ss=1;
241 var iter_id=-1;
242
243 function zoomHandler() {
244 tx = d3.event.translate[0];
245 ty = d3.event.translate[1];
246 ss = d3.event.scale;
247 updateEmbedding();
248 }
249
250 var stepnum = 0;
251
252 function stopStep() {
253 clearInterval(iter_id);
254 text = svg.selectAll("text");
255
256 // jitter function needs different data and co-ordinate representation
257 labels = d3.range(data.words.length).map(function(i) {
258 var x = (T.Y[i][0]*20*ss + tx) + mapWidth/2;
259 var y = (T.Y[i][1]*20*ss + ty) + mapHeight/2;
260 anchor_array.push({x: x, y: y, r: jitterRadius});
261 return {
262 x: x,
263 y: y,
264 name: data.words[i]
265 };
266 });
267
268 // get the actual label bounding boxes for the jitter function
269 var index = 0;
270 text.each(function() {
271 labels[index].width = this.getBBox().width;
272 labels[index].height = this.getBBox().height;
273 index += 1;
274 });
275
276
277 // setTimeout(updateEmbedding, 1);
278 // setTimeout(
279 labeler = d3.labeler()
280 .label(labels)
281 .anchor(anchor_array)
282 .width(mapWidth)
283 .height(mapHeight)
284 .update(applyJitter);
285 // .start(1000);
286
287 iter_id = setInterval(jitterStep, 1);
288 }
289
290 var jitter_i=0;
291
292 function jitterStep() {
293 if(jitter_i++ > 100) {
294 clearInterval(iter_id);
295 } else {
296 labeler.start2(10);
297 applyJitter();
298 }
299 }
300
301 var last_cost=1000;
302
303 function step() {
304 var i = T.iter;
305
306 if(i > <%= $no_iterations %>) {
307 stopStep();
308 } else {
309 var cost = Math.round(T.step() * 100000) / 100000; // do a few steps
310 $("#cost").html("tsne iteration " + i + ", cost: " + cost.toFixed(5));
311 if(i % 250 == 0 && cost >= last_cost) {
312 stopStep();
313 } else {
314 last_cost = cost;
315 updateEmbedding();
316 }
317 }
318 }
319
320 function showMap(j) {
321 data=j;
322 T.iter=0;
323 T.initDataRaw(data.vecs); // init embedding
324 drawEmbedding(); // draw initial embedding
325
326 if(iter_id >= 0) {
327 clearInterval(iter_id);
328 }
329 //T.debugGrad();
330 iter_id = setInterval(step, 1);
331 if(<%= $show_som %>) {
332 makeSOM(j, <%= $no_iterations %>);
333 }
334 }
Marc Kupietz39179ab2017-07-04 16:28:06 +0200335 var queryword;
336
337 function onload() {
338 queryword = document.getElementById('word');
339 }
340
341 function queryKorAP() {
342 window.open('http://korap.ids-mannheim.de/kalamar/?q='+queryword.value, 'KorAP');
343 }
Marc Kupietz4dc270c2017-11-24 10:17:12 +0100344
345 function queryKorAPCII(query) {
346 window.open('http://korap.ids-mannheim.de/kalamar/?ql=cosmas2&q='+query, 'KorAP');
347 }
Marc Kupietz83305222016-04-28 09:57:22 +0200348 </script>
349 </head>
Marc Kupietz39179ab2017-07-04 16:28:06 +0200350 <body onload="onload()">
Marc Kupietzb3422c12017-07-04 14:12:11 +0200351 <form method="GET">
Marc Kupietz83305222016-04-28 09:57:22 +0200352 word(s):
Marc Kupietz39179ab2017-07-04 16:28:06 +0200353 <input id="word" 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.">
Marc Kupietz2c79c5e2017-11-09 16:18:40 +0100354 cut-off:
355 <input id="cutoff" type="text" name="cutoff" size="10" value="<%= $cutoff %>" title="Only consider the most frequent x word forms.">
Marc Kupietz4ccb4892017-11-21 09:33:08 +0100356 dedupe <input type="checkbox" name="dedupe" value="1" <%= ($dedupe ? "checked" : "") %> title="radically filter out any near-duplicates">
Marc Kupietz83305222016-04-28 09:57:22 +0200357 % if($mergedEnd > 0) {
358 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.">
359 % }
Marc Kupietz2c79c5e2017-11-09 16:18:40 +0100360 max. neighbours: <input type="text" size="4" name="n" value="<%= $no_nbs %>">
361 max. iterations: <input type="text" name="N" size="4" value="<%= $no_iterations %>">
Marc Kupietz83305222016-04-28 09:57:22 +0200362 SOM <input type="checkbox" name="som" value="1" <%= ($show_som ? "checked" : "") %>>
363 % if($collocators) {
Marc Kupietz30ca4342017-11-22 21:21:20 +0100364 <span> </span>window/sort
Marc Kupietz83305222016-04-28 09:57:22 +0200365 <select name="sort">
Marc Kupietz30ca4342017-11-22 21:21:20 +0100366 <option value="0" <%= ($sort!=1 && $sort!=2? "selected":"") %>>auto focus</option>
367 <option value="1" <%= ($sort==1? "selected":"") %>>any single position</option>
368 <option value="2" <%= ($sort==2? "selected":"") %>>whole window</option>
Marc Kupietz83305222016-04-28 09:57:22 +0200369 </select>
370 % }
Marc Kupietz39179ab2017-07-04 16:28:06 +0200371 <span> </span><input type="submit" value="Show">
372 <span> </span><input type="button" value="→ KorAP" onclick="queryKorAP();" title="query word with KorAP"/>
Marc Kupietz83305222016-04-28 09:57:22 +0200373 </form>
374 <br>
Marc Kupietz694610d2017-11-25 18:30:03 +0100375 <div id="mytable"/>
Marc Kupietzf9ac54e2017-11-21 09:22:29 +0100376 % if($lists && (@$lists) > 0 && (@$lists)[0]) {
Marc Kupietz83305222016-04-28 09:57:22 +0200377 <div id="wrapper">
Marc Kupietz694610d2017-11-25 18:30:03 +0100378 <div id="first" style="width:400px">
379 <table class="display compact nowrap" id="firsttable">
380 <thead>
Marc Kupietz83305222016-04-28 09:57:22 +0200381 <tr>
382 <th align="right">#</th><th align="right">cos</th><th align="left">paradigmatic</th>
383 % if($collocators) {
Marc Kupietz694610d2017-11-25 18:30:03 +0100384 <th align="right" title="The window around the target word that is considered for summation.">w'</th>
385 <th align="right" title="Raw (max.) activation of the collocator in the output layers.">a</th>
386 <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>
Marc Kupietze7ffaf22017-11-24 10:13:08 +0100387 <th align="right">Σp/|w|</th>
388 <th title="c" align="left">collocator</th>
Marc Kupietz83305222016-04-28 09:57:22 +0200389 % }
390 </tr>
Marc Kupietz694610d2017-11-25 18:30:03 +0100391 </thead>
392 <tbody>
Marc Kupietz83305222016-04-28 09:57:22 +0200393 % my $j=0; my @words; my @vecs; my @ranks; my @marked;
394 % for my $list (@$lists) {
395 % my $i=0; while($list) {
396 % my $item = (@$list)[$i];
397 % my $c = ($collocators? (@$collocators)[$i] : 0);
398 % last if(!$c && !$item);
399 <tr>
400 <td align="right">
401 <%= ++$i %>.
402 </td>
403 % if($item) {
404 % if(!grep{$_ eq $item->{word}} @words) {
405 % push @vecs, $item->{vector};
406 % push @words, $item->{word};
407 % push @ranks, $item->{rank};
408 % push @marked, ($marked->{$item->{word}}? 1 : 0);
409 % }
410 <td align="right">
411 <%= sprintf("%.3f", $item->{dist}) %>
412 </td>
413 <td>
414 % my $class = ($marked->{$item->{word}}? "marked " : "");
415 % my $r = $item->{rank};
416 % if($r < $mergedEnd) {
417 % $class .= "merged";
418 % $r .= " (merged vocab)";
419 % } elsif($mergedEnd!=0 && $r > $mergedEnd) {
420 % $r -= $mergedEnd;
421 % }
Marc Kupietzf4b49392016-04-28 10:49:56 +0200422 <a class="<%= $class =%>"
423 title="freq. rank: <%= $r =%>"
424 href="<%= url_with->query([word => $item->{word}]) =%>">
425 <%= $item->{word} =%>
426 </a>
Marc Kupietz83305222016-04-28 09:57:22 +0200427 </td>
428 % } else {
429 <td colspan="2"/>
430 % }
431 % if($c) {
432 <td align="right">
Marc Kupietz30ca4342017-11-22 21:21:20 +0100433 <span class="mono"><%= bitvec2window($c->{pos}) %></span>
Marc Kupietz83305222016-04-28 09:57:22 +0200434 </td>
435 <td align="right">
436 <%= sprintf("%.3f", $c->{dist}) %>
437 </td>
438 <td align="right">
439 <%= sprintf("%.3e", $c->{norm}) %>
440 </td>
441 <td align="right">
442 <%= sprintf("%.3e", $c->{sum}) %>
443 </td>
444 <td align="left">
Marc Kupietz4dc270c2017-11-24 10:17:12 +0100445 <a onclick="<%= sprintf("queryKorAPCII('%s /w5 %s')", $c->{word}, $word) =%>"
Marc Kupietzb18978b2017-11-09 14:51:17 +0100446 title="freq. rank: <%= $c->{rank} =%>">
Marc Kupietz83305222016-04-28 09:57:22 +0200447 <%= $c->{word} %>
448 </td>
449 % } else {
450 <td colspan="5"/>
451 % }
452 </tr>
Marc Kupietz694610d2017-11-25 18:30:03 +0100453 % last if($i >= 100);
Marc Kupietz83305222016-04-28 09:57:22 +0200454 % }
455 % }
Marc Kupietz694610d2017-11-25 18:30:03 +0100456 </tbody>
Marc Kupietz83305222016-04-28 09:57:22 +0200457 </table>
Marc Kupietz694610d2017-11-25 18:30:03 +0100458</div>
Marc Kupietz83305222016-04-28 09:57:22 +0200459 <script>
460 % use Mojo::ByteStream 'b';
Marc Kupietzf4b49392016-04-28 10:49:56 +0200461 % my $urlprefix = url_with->query([word=>'']);
Marc Kupietz83305222016-04-28 09:57:22 +0200462 $(window).load(function() {
Marc Kupietz694610d2017-11-25 18:30:03 +0100463 showTable(<%= b(Mojo::JSON::to_json([@$collocators])) %>);
Marc Kupietzf4b49392016-04-28 10:49:56 +0200464 showMap(<%= b(Mojo::JSON::to_json({target => " $word ", mergedEnd=> $mergedEnd, words => \@words, vecs => \@vecs, ranks => \@ranks, marked => \@marked, urlprefix => $urlprefix})); %>);
Marc Kupietz83305222016-04-28 09:57:22 +0200465 });
466 </script>
Marc Kupietzf9ac54e2017-11-21 09:22:29 +0100467 % } else { # ($word && $word !~ /^\s*$/)
468 <div id="wrapper">
469 <p>
470 ERROR: "<%= $word %>" not found in vocabluary.
471 </p>
472 </div>
Marc Kupietz83305222016-04-28 09:57:22 +0200473 % }
474 <div id="second" style="width:800px; height:800px; font-family: arial;">
475 <div id="embed">
476 </div>
477 </div>
478 <div id="cost"></div>
479 % if($show_som) {
480 <div id="som2">
481 </div>
482 <div id="sominfo1"><span id="somcolor1"> </span> <span id="somword1"> </span> <span id="somcolor2"> </span> <span id="somword2"> </span> <span id="somcolor3"> </span></div>
483 <div id="sominfo">SOM iteration <span id="iterations">0</span></div>
484 % }
485 </div>
486 % if($training_args) {
487 <p>
488 Word vector model trained with <a href="https://code.google.com/p/word2vec/">word2vec</a> using the following parameters: <pre><%= $training_args %></pre>
489 </p>
490 % }
491 </body>
492</html>