Spaces:
Build error
Build error
| <html> | |
| <head> | |
| <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> | |
| <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> | |
| <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js" integrity="sha256-CMMTrj5gGwOAXBeFi7kNokqowkzbeL8ydAJy39ewjkQ=" crossorigin="anonymous"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.js" integrity="sha256-qwbDmNVLiCqkqRBpF46q5bjYH11j5cd+K+Y6D3/ja28=" crossorigin="anonymous"></script> | |
| <script src="https://unpkg.com/split.js@1.3.5/split.js" integrity="sha384-zUUFqW6+QulITI/GAXB5UnHBryKHEZp4G+eNNC1/3Z3IJ+6CZRSFcdl4f7gCa957" crossorigin="anonymous"></script> | |
| <style> | |
| [v-cloak] { | |
| display: none; | |
| } | |
| body, html { height: 100vh; } | |
| #app { | |
| border: 0; | |
| margin: 0; | |
| padding: 0; | |
| height: 100vh; | |
| width: 100vw; | |
| } | |
| table.pane { | |
| float: left; | |
| display: inline-table; | |
| border: 0; | |
| margin: 0; | |
| padding: 0; | |
| border-spacing: 0; | |
| height: 100vh; | |
| width: 50vw; | |
| } | |
| .gutter-horizontal { | |
| float: left; | |
| height: 100vh; | |
| background-color: #eee; | |
| cursor: ew-resize; | |
| } | |
| .palette, .preview { | |
| height: 100%; | |
| overflow-y: scroll; | |
| text-align: center; | |
| } | |
| .unitviz, .unitviz .modal-header, .unitviz .modal-body, .unitviz .modal-footer { | |
| font-family: Arial; | |
| font-size: 15px; | |
| } | |
| .unitgrid { | |
| text-align: center; | |
| border-spacing: 5px; | |
| border-collapse: separate; | |
| max-height: 100%; | |
| } | |
| .unitgrid .info { | |
| text-align: left; | |
| display: block; | |
| position: absolute; | |
| top: 0; | |
| color: white; | |
| } | |
| .unitgrid .layername { | |
| } | |
| .unitgrid .unitnum { | |
| } | |
| .unitlabel { | |
| font-weight: bold; | |
| line-height: 1; | |
| position: absolute; | |
| bottom: 0; | |
| color: white; | |
| white-space: nowrap; | |
| } | |
| .lowscore .unitlabel { | |
| color: silver; | |
| } | |
| .thumbcrop { | |
| overflow: hidden; | |
| width: 72px; | |
| height: 72px; | |
| } | |
| .thumbcrop img, .img-scroller img { | |
| image-rendering: pixelated; | |
| } | |
| .unit { | |
| display: inline-block; | |
| background: white; | |
| padding: 0; | |
| margin: 2px; | |
| box-shadow: 0 5px 12px grey; | |
| overflow: hidden; | |
| height: 72px; | |
| width: 72px; | |
| position: relative; | |
| user-select: none; | |
| } | |
| .unit.ablated .ablationmark::after, | |
| .selmodeablation .unit.dragged .ablationmark::after { | |
| content: '\2716'; | |
| bottom: 0; | |
| } | |
| .unit.inserted .insertionmark::after, | |
| .selmodeinsertion .unit.dragged .insertionmark::after { | |
| content: '\2713'; | |
| font-weight: bold; | |
| text-shadow: 0 0 #000; | |
| bottom: 10px; | |
| } | |
| .ablationmark::after, .insertionmark::after { | |
| line-height: 1; | |
| position: absolute; | |
| right: 0; | |
| } | |
| .unit.ablated .ablationmark { | |
| color: red; | |
| } | |
| .unit.inserted .insertionmark { | |
| color: lime; | |
| } | |
| .unit.dragged { | |
| opacity: 0.8; | |
| } | |
| .selmodeablation .unit.dragged .ablationmark, | |
| .selmodeinsertion .unit.dragged .insertionmark { | |
| color: yellow; | |
| } | |
| .iou { | |
| display: inline-block; | |
| float: right; | |
| } | |
| .modal .big-modal { | |
| width:auto; | |
| max-width:90%; | |
| max-height:80%; | |
| } | |
| .modal-title { | |
| display: inline-block; | |
| } | |
| .footer-caption { | |
| float: left; | |
| width: 100%; | |
| } | |
| .histogram { | |
| text-align: center; | |
| margin-top: 3px; | |
| } | |
| .img-wrapper { | |
| text-align: center; | |
| } | |
| .big-modal img { | |
| max-height: 60vh; | |
| } | |
| .img-scroller { | |
| overflow-x: scroll; | |
| } | |
| .img-scroller .img-fluid { | |
| max-width: initial; | |
| } | |
| .gridheader { | |
| font-size: 12px; | |
| margin-bottom: 10px; | |
| margin-left: 30px; | |
| margin-right: 30px; | |
| } | |
| .gridheader:after { | |
| content: ''; | |
| display: table; | |
| clear: both; | |
| } | |
| .sortheader { | |
| float: right; | |
| cursor: default; | |
| } | |
| .layerinfo { | |
| float: left; | |
| } | |
| .sortby { | |
| text-decoration: underline; | |
| cursor: pointer; | |
| } | |
| .sortby.currentsort { | |
| text-decoration: none; | |
| font-weight: bold; | |
| cursor: default; | |
| } | |
| .bg-inverse { | |
| background: #021B54; | |
| } | |
| .layout { | |
| width: 50%; | |
| vertical-align: top; | |
| } | |
| .chooser-cell { | |
| height: 100px; | |
| background: #021B54; | |
| color: white; | |
| padding: 5px 5px 0; | |
| } | |
| .examplepair { | |
| display: inline-block; | |
| margin: 2px; | |
| position: relative; | |
| line-height: 0; | |
| } | |
| .exampleid { | |
| position: absolute; | |
| line-height: 1; | |
| font-size: large; | |
| font-weight: bold; | |
| text-shadow: -1px 2px #000, 1px 2px #000; | |
| bottom: 0; | |
| color: white; | |
| z-index: 1; | |
| } | |
| .querymark, .editmark { | |
| position: absolute; | |
| background: rgba(255,0,0,0.5); | |
| width: 10px; | |
| height: 10px; | |
| margin: -5px; | |
| border-radius: 5px; | |
| top: 0; | |
| left: 0; | |
| pointer-events: none; | |
| } | |
| .editmark { | |
| background: rgba(0,255,0,0.5); | |
| } | |
| .chooser-cell input[type=button] { | |
| border-radius: 5px; padding: 0 5px; | |
| } | |
| </style> | |
| </head> | |
| <body class="unitviz"> | |
| <div id="app" :class="['selmode' + selection_mode]" v-cloak> | |
| <table class="pane" id="leftpane"> | |
| <tr> | |
| <td class="chooser-cell"> | |
| <div v-if="dissect"> | |
| <p> | |
| <label for="selmode-ablation-radio"> | |
| <input type="radio" id="selmode-ablation-radio" | |
| value="ablation" v-model="selection_mode"> | |
| <span style="color:red">✖</span> | |
| Choose units to ablate.</label> | |
| <label for="selmode-insertion-radio"> | |
| <input type="radio" id="selmode-insertion-radio" | |
| value="insertion" v-model="selection_mode"> | |
| <span style="color:lime;font-weight:bold">✓</span> | |
| Choose units to insert.</label> | |
| </p> | |
| <label for="ranking-radio"> | |
| <input type="radio" id="ranking-radio" value="ranking" v-model="palette_mode"> | |
| Show all units in | |
| <select v-model="palette_layer" v-on:change="palette_mode='ranking'"> | |
| <option v-for="(lrec, lind) in dissect.layers" :value="lind" | |
| >{{lrec.layer}}</option> | |
| </select>, | |
| sorted by | |
| <template v-for="lrec in [dissect.layers[palette_layer]]"> | |
| <select v-model="sort_order" v-on:change="palette_mode='ranking'"> | |
| <option v-for="rank in lrec['rankings']" :value="rank.name" | |
| >{{rank.name}}</option> | |
| </select> | |
| </template> | |
| </label><br> | |
| <label for="ablation-radio"> | |
| <input type="radio" id="ablation-radio" value="ablation" v-model="palette_mode"> | |
| Show current ablation | |
| ({{ _.sum(_.map(selected_ablation, | |
| function(x) { return _.sum(x)} )) | |
| }} units ablated)</label> | |
| <input type="button" value="Reset" v-on:click="resetselection('ablation')"> | |
| <input type="button" :value="'Invert ' + dissect.layers[palette_layer].layer" | |
| v-on:click="invertselection('ablation')"><br> | |
| <label for="insertion-radio"> | |
| <input type="radio" id="insertion-radio" value="insertion" | |
| v-model="palette_mode"> | |
| Show current insertion | |
| ({{ _.sum(_.map(selected_insertion, | |
| function(x) { return _.sum(x)} )) | |
| }} units inserted)</label> | |
| <input type="button" value="Reset" v-on:click="resetselection('insertion')"> | |
| <input type="button" :value="'Invert ' + dissect.layers[palette_layer].layer" | |
| v-on:click="invertselection('insertion')"><br> | |
| <label for="query-radio"> | |
| <input type="radio" id="query-radio" value="query" v-model="palette_mode" | |
| :disabled="!(dissect.layers[palette_layer].layer in query_ranking)"> | |
| Units in {{ dissect.layers[palette_layer].layer }} | |
| by | |
| <select v-model="query_stat"> | |
| <option value="mean_quantile">quantile of average</option> | |
| <option value="max_quantile">quantile of maximum</option> | |
| <option value="mean">average activation</option> | |
| <option value="max">maximum activation</option> | |
| </select> | |
| activation on <span style="color:red">●</span> selected pixels of | |
| {{ currentquery.map(x => 'image #' + x.id).join(', ') || 'an image' }}. | |
| </label> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="layout"> | |
| <div class="palette" v-if="dissect"> | |
| <div class="unitgrid" | |
| ><div :class="{unit: true, lowscore: !urec.interp, | |
| ablated: (selected_ablation[urec.layer][urec.unit] > 0), | |
| inserted: (selected_insertion[urec.layer][urec.unit] > 0), | |
| dragged: (dragging.active && | |
| ((dragging.first <= ordernum && ordernum <= dragging.last) || | |
| (dragging.last <= ordernum && ordernum <= dragging.first))) | |
| }" | |
| v-for="urec, ordernum in palette_units" | |
| :data-ordernum="ordernum" | |
| > | |
| <template v-if="sort_order.indexOf('-') < 0" | |
| ><div v-if="'iou_label' in urec" class="unitlabel" | |
| >{{urec.iou_label}}</div></template> | |
| <div class="info" | |
| ><span class="layername">{{urec.layer}}</span | |
| > <span class="unitnum">{{urec.unit}}</span> | |
| </div> | |
| <div class="thumbcrop" | |
| ><img | |
| :src="urec.dirname + '/s-image/' + urec.unit + '-top.jpg'" | |
| height="72" | |
| ></div> | |
| <div class="ablationmark"></div> | |
| <div class="insertionmark"></div> | |
| </div> <!-- end unit --> | |
| </div> <!-- end unitgrid --> | |
| </div> <!-- end palette --> | |
| </td> | |
| </tr> | |
| </table> <!-- end pane --> | |
| <table class="pane" id="rightpane"> | |
| <tr> | |
| <td rowspan="2" class="layout"> | |
| <div class="preview" v-if="dissect"> | |
| <p>Seeds to generate <input size="30" v-model="image_numbers"></p> | |
| <p style="text-align: left"> | |
| To transfer activations from one pixel to another (1) click on a source pixel | |
| on the left image and (2) click on a target pixel on a right image, | |
| then (3) choose a set of units to insert in the palette.</p> | |
| <div v-for="ex in examples" class="examplepair"> | |
| <div class="exampleid">#{{ ex.id }}</div> | |
| <img :src="ex.baseline" style="max-width:50%;" | |
| :data-imgid="ex.id" | |
| data-side="left" | |
| v-on:click="clickexample" | |
| ><img :src="ex.modified" style="max-width:50%;" | |
| :data-imgid="ex.id" | |
| data-side="right" | |
| v-on:click="clickexample" | |
| ><div class="querymark" v-if="ex.mask" :style="{ | |
| top: ((ex.mask.bitbounds[0] + ex.mask.bitbounds[2]) / 0.02 | |
| / ex.mask.shape[0]) + '%', | |
| left: ((ex.mask.bitbounds[1] + ex.mask.bitbounds[3]) / 0.04 | |
| / ex.mask.shape[1]) + '%'}" | |
| ></div><div class="editmark" v-if="ex.edit" :style="{ | |
| top: ((ex.edit.bitbounds[0] + ex.edit.bitbounds[2]) / 0.02 | |
| / ex.edit.shape[0]) + '%', | |
| left: ((ex.edit.bitbounds[1] + ex.edit.bitbounds[3]) / 0.04 | |
| / ex.edit.shape[1]) + 50 + '%'}" | |
| ></div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| </table> | |
| </div> <!-- end app --> | |
| <div class="modal" id="lightbox"> | |
| <div class="modal-dialog big-modal" role="document"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title"></h5> | |
| <button type="button" class="close" | |
| data-dismiss="modal" aria-label="Close"> | |
| <span aria-hidden="true">×</span> | |
| </button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="img-wrapper img-scroller"> | |
| <img class="fullsize img-fluid"> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <div class="footer-caption"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| $(document).on('click', '[data-toggle=lightbox]', function(event) { | |
| $('#lightbox img').attr('src', $(this).attr('href')); | |
| $('#lightbox .modal-title').text($(this).data('title') || | |
| $(this).closest('.unit').find('.unitlabel').text()); | |
| $('#lightbox .footer-caption').text($(this).data('footer') || | |
| $(this).closest('.unit').find('.info').text()); | |
| event.preventDefault(); | |
| $('#lightbox').modal(); | |
| $('#lightbox img').closest('div').scrollLeft(0); | |
| }); | |
| $(document).on('mousedown', '.unit', function(event) { | |
| if (!(event.buttons & 1)) return; | |
| var layer = $(event.currentTarget).find('.info .layername').text(); | |
| var unit = $(event.currentTarget).closest('.unit').data('ordernum'); | |
| theapp.dragging.active = true; | |
| theapp.dragging.first = unit; | |
| theapp.dragging.last = unit; | |
| event.preventDefault(); | |
| }); | |
| $(document).on('mouseup', function(event) { | |
| if (!theapp.dragging.active) { return; } | |
| theapp.dragging.active = false; | |
| var first_sel = $('.unit').filter(function(i, elt) { | |
| return elt.dataset.ordernum == theapp.dragging.first; | |
| }).map(function(i, elt) { | |
| return {layer: $(elt).find('.layername').text(), | |
| unit: $(elt).find('.unitnum').text()}; })[0]; | |
| var selected = $('.unit').filter(function(i, elt) { | |
| return (theapp.dragging.first <= elt.dataset.ordernum && | |
| elt.dataset.ordernum <= theapp.dragging.last) || | |
| (theapp.dragging.last <= elt.dataset.ordernum && | |
| elt.dataset.ordernum <= theapp.dragging.first); }) | |
| .map(function(i, elt) { | |
| return {layer: $(elt).find('.layername').text(), | |
| unit: $(elt).find('.unitnum').text()}; }); | |
| if (selected.length) { | |
| var selection = 'selected_' + theapp.selection_mode; | |
| var mode = 1 - theapp[selection][first_sel.layer][first_sel.unit]; | |
| for (u of selected) { | |
| theapp[selection][u.layer].splice(u.unit, 1, mode); | |
| } | |
| theapp.selectionchange(); | |
| } | |
| }); | |
| $(document).on('mouseenter', '.unit', function(event) { | |
| if (!(event.buttons & 1)) { theapp.dragging.active = false; } | |
| if (!theapp.dragging.active) return; | |
| theapp.dragging.last = | |
| $(event.currentTarget).closest('.unit').data('ordernum'); | |
| }); | |
| $(function() { | |
| window.Split(['#leftpane', '#rightpane'], { | |
| sizes: [50, 50], | |
| minSize: 280, | |
| snapOffset: 0, | |
| }); | |
| }); | |
| var theapp = new Vue({ | |
| el: '#app', | |
| data: { | |
| palette_mode: 'ranking', | |
| sort_order: 'unit', | |
| palette_layer: null, | |
| selected_ablation: null, | |
| selected_insertion: null, | |
| selection_mode: 'ablation', | |
| dragging: { active: false, first: null, last: null }, | |
| recipe: null, | |
| dissect: null, | |
| image_numbers: '10-19', | |
| examples: _.range(10, 20).map(function(x) { | |
| return {id: x, baseline: '', modified: '', mask: null, edit: null}; }), | |
| query_stat: 'mean_quantile', | |
| query_ranking: {}, | |
| }, | |
| created: function() { | |
| var self = this; | |
| $.getJSON('dissect.json?' + Math.random(), function(d) { | |
| self.selected_ablation = {}; | |
| self.selected_insertion = {}; | |
| for (var layer of d.layers) { | |
| // Preprocess ranking records to sort them. | |
| for (var rank of layer.rankings) { | |
| if (!('ranking' in rank)) { | |
| rank.ranking = rank.score.map((score, index) => [score, index]) | |
| .sort(([score1], [score2]) => score1 - score2) | |
| .map(([, index]) => index); | |
| } | |
| } | |
| // Note layer in each unit record to simplify mixing. | |
| for (var urec of layer.units) { | |
| urec.layer = layer.layer; | |
| urec.dirname = layer.dirname; | |
| } | |
| Vue.set(self.selected_ablation, layer.layer, | |
| _.fill(Array(layer.units.length), 0)); | |
| Vue.set(self.selected_insertion, layer.layer, | |
| _.fill(Array(layer.units.length), 0)); | |
| } | |
| self.dissect = d; | |
| self.sort_order = d.default_ranking; | |
| self.palette_layer = Math.floor((d.layers.length - 1) / 2); | |
| }); | |
| this.selectionchange(); | |
| }, | |
| computed: { | |
| currentquery: function() { | |
| var regions = []; | |
| for (var ex of this.examples) { | |
| if (ex.mask) { | |
| regions.push({id: ex.id, mask: ex.mask}); | |
| } | |
| } | |
| return regions; | |
| }, | |
| palette_units: function() { | |
| if (this.palette_mode == 'ranking') { | |
| // Order units according to an iou-matching sort order. | |
| var lrec = this.dissect.layers[this.palette_layer]; | |
| var ranking = _.find(lrec.rankings, x=>x.name == this.sort_order); | |
| return ranking.ranking.map(x => lrec.units[x]); | |
| } else if (this.palette_mode == 'ablation' || | |
| this.palette_mode == 'insertion') { | |
| // Show units involved in the edit | |
| var result = []; | |
| var selectionname = 'selected_' + this.palette_mode; | |
| for (var lrec of this.dissect.layers) { | |
| var sel = this[selectionname][lrec.layer]; | |
| for (var u in sel) { | |
| if (sel[u] > 0) { | |
| result.push(lrec.units[u]); | |
| } | |
| } | |
| } | |
| return result; | |
| } else if (this.palette_mode == 'query') { | |
| // Order units according to query ranking | |
| var lrec = this.dissect.layers[this.palette_layer]; | |
| var ranking = this.query_ranking[lrec.layer]; | |
| return ranking.ranking.map(x => lrec.units[x]); | |
| } | |
| } | |
| }, | |
| watch: { | |
| query_stat: function() { | |
| this.querychange(); | |
| }, | |
| palette_layer: function(val) { | |
| // If sort_order is not available at this layer, reset it to default. | |
| var self = this; | |
| if (!_.find(self.dissect.layers[val].rankings, | |
| function(x) { return x.name == self.sort_order; })) { | |
| self.sort_order = self.dissect.default_ranking; | |
| } | |
| }, | |
| image_numbers: function(val) { | |
| // Parse a series of image numbers | |
| var max_examples = 1000; | |
| var rs = val.replace(/[^-\d]+/g, ' ').replace(/\s*-\s*/g, '-').split(' '); | |
| var indexes = []; | |
| for (var r of rs) { | |
| if (r.match(/\d+-\d+/)) { | |
| for (var i = parseInt(r.split('-')[0]); | |
| i <= parseInt(r.split('-')[1]); i++) { | |
| indexes.push(i); | |
| } | |
| } else if (r.match(/\d+/)) { | |
| indexes.push(parseInt(r)); | |
| } else if (indexes.length == 0) { | |
| indexes.push(0); | |
| } | |
| if (indexes.length >= max_examples) { break; } | |
| } | |
| // Update examples to match. | |
| var modified = false; | |
| var examples = this.examples; | |
| for (i = 0; i < indexes.length; i++) { | |
| if (i >= examples.length) { | |
| examples.push({id: indexes[i], baseline: '', modified: '', | |
| mask: null }); | |
| modified = true | |
| } else if (examples[i].id != indexes[i]) { | |
| examples[i].id = indexes[i]; | |
| examples[i].baseline = ''; | |
| examples[i].modified = ''; | |
| examples[i].mask = null; | |
| examples[i].edit = null; | |
| modified = true | |
| } | |
| } | |
| if (examples.length > indexes.length) { | |
| examples.splice(indexes.length, examples.length - indexes.length); | |
| modified = true | |
| } | |
| if (modified) { | |
| this.generate_examples([], _.range(examples.length), true); | |
| this.selectionchange(); | |
| } | |
| } | |
| }, | |
| methods: { | |
| hashchange: function() { | |
| this.palette_layer = +window.location.hash.substr(1) || 0; | |
| }, | |
| resetselection: function(mode) { | |
| var selection = 'selected_' + (mode); | |
| console.log(selection); | |
| for (var layer in this[selection]) { | |
| this[selection][layer] = _.fill(Array(this[selection][layer].length),0); | |
| } | |
| this.selectionchange(); | |
| }, | |
| invertselection: function(mode) { | |
| var layer = this.dissect.layers[this.palette_layer].layer; | |
| var selection = 'selected_' + (mode); | |
| var sel = this[selection][layer]; | |
| for (var u in sel) { | |
| sel.splice(u, 1, 1 - sel[u]); | |
| } | |
| this.selectionchange(); | |
| }, | |
| selectionchange: function() { | |
| var edited_indices = []; | |
| var unedited_indices = []; | |
| var ca = this.currentablations(); | |
| for (var i in this.examples) { | |
| var ci = this.currentinsertion(ca, i); | |
| if (ci) { | |
| edited_indices.push({intervention: ci, index: i}); | |
| } else{ | |
| unedited_indices.push(i); | |
| } | |
| } | |
| if (!window.skipupdate && unedited_indices.length) { | |
| this.generate_examples(ca, unedited_indices, false); | |
| } | |
| for (var r of edited_indices) { | |
| this.generate_examples(r.intervention, [r.index], false); | |
| } | |
| }, | |
| currentablations: function() { | |
| var ablations = []; | |
| if (this.selected_ablation) { | |
| for (var layer in this.selected_ablation) { | |
| for (var unit in this.selected_ablation[layer]) { | |
| if (this.selected_ablation[layer][unit] > 0) { | |
| ablations.push({ | |
| layer: layer, | |
| unit: parseInt(unit), | |
| alpha: this.selected_ablation[layer][unit], | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| return ablations.length ? [{ablations: ablations}] : []; | |
| }, | |
| currentinsertion: function(currentablations, exindex) { | |
| if (!this.examples[exindex].edit || !this.selected_insertion) { | |
| return null; | |
| } | |
| var insertions = []; | |
| for (var layer in this.selected_insertion) { | |
| for (var unit in this.selected_insertion[layer]) { | |
| if (this.selected_insertion[layer][unit] > 0) { | |
| insertions.push({ | |
| layer: layer, | |
| unit: parseInt(unit), | |
| alpha: this.selected_insertion[layer][unit], | |
| value: this.query_ranking && layer in this.query_ranking && | |
| this.query_ranking[layer].activation[unit] || 0 | |
| }); | |
| } | |
| } | |
| } | |
| var result = _.clone(currentablations); | |
| result.push({ablations: insertions, mask: this.examples[exindex].edit}); | |
| return result; | |
| }, | |
| querychange: function() { | |
| this.query_regions(this.currentquery, this.currentablations()); | |
| }, | |
| query_regions: function(regions, intervention) { | |
| if (!_.keys(regions).length) { | |
| return; | |
| } | |
| var ids = regions.map(x => x.id); | |
| var masks = regions.map(x => x.mask); | |
| var self = this; | |
| $.post({ | |
| url: '/api/features', | |
| data: JSON.stringify({ | |
| project: this.currentproject(), | |
| ids: ids, | |
| masks: masks, | |
| // layers: [this.dissect.layers[this.palette_layer].layer], | |
| layers: this.dissect.layers.map(x => x.layer), | |
| // interventions: intervention, | |
| }), | |
| headers: { | |
| "Content-type": "application/json; charset=UTF-8" | |
| }, | |
| success: function(resp) { | |
| var statname = self.query_stat; | |
| var actname = statname.replace('_quantile', ''); | |
| var stats = resp.res; | |
| var result = {}; | |
| for (var layer in stats) { | |
| result[layer] = { | |
| score: stats[layer][statname], | |
| ranking: stats[layer][statname] | |
| .map((score, index) => [score, index]) | |
| .sort(([score1], [score2]) => score2 - score1) | |
| .map(([, index]) => index), | |
| activation: stats[layer][actname]}; | |
| } | |
| self.query_ranking = result; | |
| self.palette_mode = 'query'; | |
| // If there are any insertions, now the edit has changed | |
| if (_.sum(_.map(self.selected_insertion, x => _.sum(x))) > 0) { | |
| self.selectionchange(); | |
| } | |
| }, | |
| dataType: 'json' | |
| }); | |
| }, | |
| clickexample: function(ev) { | |
| var elt = ev.currentTarget; | |
| var imgid = elt.dataset.imgid * 1; | |
| var side = elt.dataset.side; | |
| var w = elt.naturalWidth; | |
| var h = elt.naturalHeight; | |
| var x = Math.round((ev.pageX - $(elt).offset().left) * (w / elt.width)); | |
| var y = Math.round((ev.pageY - $(elt).offset().top) * (h / elt.height)); | |
| // Clear all masks and leave one mask with one pixel chosen. | |
| var field = (side == 'right') ? 'edit' : 'mask' | |
| for (var ex of this.examples) { | |
| if (ex.id != imgid) { | |
| Vue.set(ex, field, null); | |
| } else { | |
| Vue.set(ex, field, { | |
| shape: [h, w], | |
| bitbounds: [y, x, y+1, x+1], | |
| bitstring: '1' | |
| }); | |
| } | |
| } | |
| if (field == 'edit') { | |
| this.selection_mode = 'insertion'; | |
| this.selectionchange(); | |
| } else { | |
| this.querychange(); | |
| } | |
| }, | |
| generate_examples: function(intervention, example_indexes, baseline_only) { | |
| var self = this; | |
| var ids = example_indexes.map(x => self.examples[x].id); | |
| $.post({ | |
| url: '/api/generate', | |
| data: JSON.stringify({ | |
| project: this.currentproject(), | |
| wantz: 0, | |
| ids: ids, | |
| interventions: intervention | |
| }), | |
| headers: { | |
| "Content-type": "application/json; charset=UTF-8" | |
| }, | |
| success: function(resp) { | |
| for (var j in resp.res) { | |
| var i = example_indexes[j]; | |
| if (self.examples[i].id == ids[j]) { | |
| if (!baseline_only) { | |
| self.examples[i].modified = resp.res[j].d; | |
| } | |
| if (!intervention || !intervention.length) { | |
| self.examples[i].baseline = resp.res[j].d; | |
| } | |
| } | |
| } | |
| }, | |
| dataType: 'json' | |
| }); | |
| }, | |
| currentproject: function() { | |
| return location.pathname.match(/\/data\/([^\/]*)/)[1]; | |
| } | |
| }, | |
| filters: { | |
| fixed: function(value, digits, truncate) { | |
| if (typeof value != 'number') return value; | |
| var fixed = value.toFixed(digits); | |
| return truncate ? +fixed : fixed; | |
| } | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |