bhoov commited on
Commit
fe4a287
1 Parent(s): ea2c8dc

Make server/component frontend pair

Browse files
client/dist/exBERT.html CHANGED
@@ -34,189 +34,7 @@
34
 
35
  </div>
36
 
37
- <div class="main-grid">
38
- <div class="left-half vpartial-95 scrolling">
39
-
40
- <div class="text-center">
41
-
42
- <div id="sentence-input">
43
- <form>
44
- <div class="form-group">
45
- <label for="form-sentence-a"> Input Sentence </label>
46
- <input id="form-sentence-a" type="text" name="sent-a-input"> </p>
47
- </div>
48
- <div class="padding"></div>
49
- <button class="btn btn-primary" id="update-sentence" type="button">Update</button>
50
- </form>
51
- </div>
52
- <hr />
53
-
54
- <div id="connector-container">
55
-
56
- <div class="connector-controls">
57
- <div class="left-control-half">
58
- <div id="model-selection">
59
- <label for="model-options">Select model: </label>
60
- <select id="model-option-selector" name="model-options"></select>
61
- </div>
62
- <div class="slide-container">
63
- <div>
64
- <label for="my-range">
65
- Display top <span id="my-range-value">…</span>% of attention
66
- </label>
67
- <input type="range" min="0" max="100" value="70" class="slider" id="my-range"> <br>
68
- </div>
69
- </div>
70
-
71
- <div id="layer-selection">
72
- <div class="input-description">
73
- Layer:
74
- </div>
75
-
76
- <div class="layer-select btn-group btn-group-toggle" data-toggle="buttons"
77
- id="layer-select"> </div>
78
- </div>
79
- <div id="cls-toggle">
80
- <div class="input-description">
81
- Hide Special Tokens
82
- </div>
83
-
84
- <label class="switch">
85
- <input type="checkbox" checked='checked'>
86
- <span class="short-slider round"></span>
87
- </label>
88
- </div>
89
- </div>
90
-
91
- <div class="head-control">
92
- <div id="selected-head-display">
93
- <div class="input-description">
94
- Selected heads:
95
- </div>
96
- <div id="selected-heads"></div>
97
- </div>
98
-
99
- <div class="select-input" id="head-all-or-none">
100
- <button id="select-all-heads">Select all heads</button>
101
- <button id="select-no-heads">Unselect all heads</button>
102
- </div>
103
-
104
- <div id="usage-info">
105
- <p> You focus on one token by <b>click</b>.<br />
106
- You can mask any token by <b>double click</b>.</p>
107
- <p>You can select and de-select a head by a <b>click</b> on the heatmap columns</p>
108
-
109
- </div>
110
- </div>
111
-
112
- </div>
113
-
114
- <div id=vis-break></div>
115
-
116
-
117
- <div class="text-center" id="atn-container">
118
- <div id="head-info-box"></div>
119
- <svg id="left-att-heads"></svg>
120
- <div id="left-tokens"></div>
121
- <svg id="atn-display"></svg>
122
- <div id="right-tokens"></div>
123
- <svg id="right-att-heads"></svg>
124
- </div>
125
-
126
- </div>
127
-
128
- <!-- Part II of HTML -->
129
- <hr />
130
-
131
- <div id="corpus-selection-description">
132
- <header>
133
- <!-- Search <span class="inline-select" id="corpus-select">corpus</span> -->
134
- Search <select id="corpus-select"></select>
135
- </header>
136
- </div>
137
-
138
- <div id="corpus-querying">
139
- <form>
140
- <button class="btn btn-primary" id="search-contexts" type="button">by Context</button>
141
- <button class="btn btn-primary" id="search-embeddings" type="button">by Embedding</button>
142
- </form>
143
- </div>
144
-
145
- <div id="histograms">
146
- <div id="matched-histogram">
147
- <svg class="histogram" id="matched-histogram-container"></svg>
148
- <div class="pos-selector">
149
- <span id="match-kind">Matched</span> Word Summary:
150
- <div id="matched-meta-select" class="btn-group btn-group-toggle" data-toggle="buttons">
151
- <label class="btn btn-secondary active" value="pos">
152
- <input type="radio" name="options" id="option1" autocomplete="off" value="pos"> POS
153
- </label>
154
- <label class="btn btn-secondary" value="dep">
155
- <input type="radio" name="options" id="option2" autocomplete="off"> DEP
156
- </label>
157
- <label class="btn btn-secondary" value="is_ent">
158
- <input type="radio" name="options" id="option3" autocomplete="off"> ENT
159
- </label>
160
- </div>
161
- </div>
162
- </div>
163
-
164
- <div id="max-att-histogram">
165
- <svg class="histogram" id="max-att-histogram-container"></svg>
166
- <div class="pos-selector">
167
- Max Attention Summary:
168
-
169
- <div id="max-att-meta-select" class="btn-group btn-group-toggle" data-toggle="buttons">
170
- <label class="btn btn-secondary active" value="pos">
171
- <input type="radio" name="options" autocomplete="off" value="pos"> POS
172
- </label>
173
- <label class="btn btn-secondary" value="dep">
174
- <input type="radio" name="options" autocomplete="off"> DEP
175
- </label>
176
- <label class="btn btn-secondary" value="is_ent">
177
- <input type="radio" name="options" autocomplete="off"> ENT
178
- </label>
179
- <label class="btn btn-secondary" value="offset">
180
- <input type="radio" name="options" autocomplete="off"> OFFSET
181
- </label>
182
- </div>
183
- <!-- <select name="position-meta-dropdown" id="position-meta-dropdown">
184
- <option value="offset">OFFSET</option>
185
- </select> -->
186
- </div>
187
- </div>
188
- </div>
189
- </div>
190
-
191
- </div>
192
-
193
- <div class="vertical-separator"></div>
194
-
195
-
196
- <div class="right-half">
197
- <div id="corpus-vis">
198
- <div id="corpus-control-buttons">
199
- <button class="btn btn-xs btn-secondary" id="minus-left" type="button">+</button>
200
- <button class="btn btn-xs btn-danger" id="kill-left" type="button">-</button>
201
- <span>&larr;||&rarr;</span>
202
- <button class="btn btn-xs btn-danger" id="kill-right" type="button">-</button>
203
- <button class="btn btn-xs btn-secondary" id="plus-right" type="button">+</button>
204
- <button class="btn btn-xs btn-info" id="mat-refresh" type="button">&#8635;</button>
205
- </div>
206
-
207
-
208
- <div class="vpartial-90 scrolling">
209
- <div class="whitespace"></div>
210
- <div id="corpus-msg-box"></div>
211
- <div id="main-corpus-vis">
212
- <div id="corpus-mat-container"></div>
213
- <div id="corpus-similar-sentences-div"></div>
214
- </div>
215
- </div>
216
- </div>
217
-
218
- </div>
219
- </div>
220
 
221
  <script src="vendor.js"></script>
222
  <script src="main.js"></script>
 
34
 
35
  </div>
36
 
37
+ <div id="attention-vis"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  <script src="vendor.js"></script>
40
  <script src="main.js"></script>
client/dist/main.js CHANGED
The diff for this file is too large to render. See raw diff
 
client/src/exBERT.html CHANGED
@@ -34,189 +34,7 @@
34
 
35
  </div>
36
 
37
- <div class="main-grid">
38
- <div class="left-half vpartial-95 scrolling">
39
-
40
- <div class="text-center">
41
-
42
- <div id="sentence-input">
43
- <form>
44
- <div class="form-group">
45
- <label for="form-sentence-a"> Input Sentence </label>
46
- <input id="form-sentence-a" type="text" name="sent-a-input"> </p>
47
- </div>
48
- <div class="padding"></div>
49
- <button class="btn btn-primary" id="update-sentence" type="button">Update</button>
50
- </form>
51
- </div>
52
- <hr />
53
-
54
- <div id="connector-container">
55
-
56
- <div class="connector-controls">
57
- <div class="left-control-half">
58
- <div id="model-selection">
59
- <label for="model-options">Select model: </label>
60
- <select id="model-option-selector" name="model-options"></select>
61
- </div>
62
- <div class="slide-container">
63
- <div>
64
- <label for="my-range">
65
- Display top <span id="my-range-value">…</span>% of attention
66
- </label>
67
- <input type="range" min="0" max="100" value="70" class="slider" id="my-range"> <br>
68
- </div>
69
- </div>
70
-
71
- <div id="layer-selection">
72
- <div class="input-description">
73
- Layer:
74
- </div>
75
-
76
- <div class="layer-select btn-group btn-group-toggle" data-toggle="buttons"
77
- id="layer-select"> </div>
78
- </div>
79
- <div id="cls-toggle">
80
- <div class="input-description">
81
- Hide Special Tokens
82
- </div>
83
-
84
- <label class="switch">
85
- <input type="checkbox" checked='checked'>
86
- <span class="short-slider round"></span>
87
- </label>
88
- </div>
89
- </div>
90
-
91
- <div class="head-control">
92
- <div id="selected-head-display">
93
- <div class="input-description">
94
- Selected heads:
95
- </div>
96
- <div id="selected-heads"></div>
97
- </div>
98
-
99
- <div class="select-input" id="head-all-or-none">
100
- <button id="select-all-heads">Select all heads</button>
101
- <button id="select-no-heads">Unselect all heads</button>
102
- </div>
103
-
104
- <div id="usage-info">
105
- <p> You focus on one token by <b>click</b>.<br />
106
- You can mask any token by <b>double click</b>.</p>
107
- <p>You can select and de-select a head by a <b>click</b> on the heatmap columns</p>
108
-
109
- </div>
110
- </div>
111
-
112
- </div>
113
-
114
- <div id=vis-break></div>
115
-
116
-
117
- <div class="text-center" id="atn-container">
118
- <div id="head-info-box"></div>
119
- <svg id="left-att-heads"></svg>
120
- <div id="left-tokens"></div>
121
- <svg id="atn-display"></svg>
122
- <div id="right-tokens"></div>
123
- <svg id="right-att-heads"></svg>
124
- </div>
125
-
126
- </div>
127
-
128
- <!-- Part II of HTML -->
129
- <hr />
130
-
131
- <div id="corpus-selection-description">
132
- <header>
133
- <!-- Search <span class="inline-select" id="corpus-select">corpus</span> -->
134
- Search <select id="corpus-select"></select>
135
- </header>
136
- </div>
137
-
138
- <div id="corpus-querying">
139
- <form>
140
- <button class="btn btn-primary" id="search-contexts" type="button">by Context</button>
141
- <button class="btn btn-primary" id="search-embeddings" type="button">by Embedding</button>
142
- </form>
143
- </div>
144
-
145
- <div id="histograms">
146
- <div id="matched-histogram">
147
- <svg class="histogram" id="matched-histogram-container"></svg>
148
- <div class="pos-selector">
149
- <span id="match-kind">Matched</span> Word Summary:
150
- <div id="matched-meta-select" class="btn-group btn-group-toggle" data-toggle="buttons">
151
- <label class="btn btn-secondary active" value="pos">
152
- <input type="radio" name="options" id="option1" autocomplete="off" value="pos"> POS
153
- </label>
154
- <label class="btn btn-secondary" value="dep">
155
- <input type="radio" name="options" id="option2" autocomplete="off"> DEP
156
- </label>
157
- <label class="btn btn-secondary" value="is_ent">
158
- <input type="radio" name="options" id="option3" autocomplete="off"> ENT
159
- </label>
160
- </div>
161
- </div>
162
- </div>
163
-
164
- <div id="max-att-histogram">
165
- <svg class="histogram" id="max-att-histogram-container"></svg>
166
- <div class="pos-selector">
167
- Max Attention Summary:
168
-
169
- <div id="max-att-meta-select" class="btn-group btn-group-toggle" data-toggle="buttons">
170
- <label class="btn btn-secondary active" value="pos">
171
- <input type="radio" name="options" autocomplete="off" value="pos"> POS
172
- </label>
173
- <label class="btn btn-secondary" value="dep">
174
- <input type="radio" name="options" autocomplete="off"> DEP
175
- </label>
176
- <label class="btn btn-secondary" value="is_ent">
177
- <input type="radio" name="options" autocomplete="off"> ENT
178
- </label>
179
- <label class="btn btn-secondary" value="offset">
180
- <input type="radio" name="options" autocomplete="off"> OFFSET
181
- </label>
182
- </div>
183
- <!-- <select name="position-meta-dropdown" id="position-meta-dropdown">
184
- <option value="offset">OFFSET</option>
185
- </select> -->
186
- </div>
187
- </div>
188
- </div>
189
- </div>
190
-
191
- </div>
192
-
193
- <div class="vertical-separator"></div>
194
-
195
-
196
- <div class="right-half">
197
- <div id="corpus-vis">
198
- <div id="corpus-control-buttons">
199
- <button class="btn btn-xs btn-secondary" id="minus-left" type="button">+</button>
200
- <button class="btn btn-xs btn-danger" id="kill-left" type="button">-</button>
201
- <span>&larr;||&rarr;</span>
202
- <button class="btn btn-xs btn-danger" id="kill-right" type="button">-</button>
203
- <button class="btn btn-xs btn-secondary" id="plus-right" type="button">+</button>
204
- <button class="btn btn-xs btn-info" id="mat-refresh" type="button">&#8635;</button>
205
- </div>
206
-
207
-
208
- <div class="vpartial-90 scrolling">
209
- <div class="whitespace"></div>
210
- <div id="corpus-msg-box"></div>
211
- <div id="main-corpus-vis">
212
- <div id="corpus-mat-container"></div>
213
- <div id="corpus-similar-sentences-div"></div>
214
- </div>
215
- </div>
216
- </div>
217
-
218
- </div>
219
- </div>
220
 
221
  <script src="vendor.js"></script>
222
  <script src="main.js"></script>
 
34
 
35
  </div>
36
 
37
+ <div id="attention-vis"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  <script src="vendor.js"></script>
40
  <script src="main.js"></script>
client/src/ts/api/mainApi.ts CHANGED
@@ -134,56 +134,4 @@ export class API {
134
 
135
  return checkDemoAPI(toSend, url, payload)
136
  }
137
-
138
- /**
139
- *
140
- * @param embedding Embedding of the word
141
- * @param layer In the l'th layer
142
- * @param k how many results to retrieve
143
- */
144
- getNearestEmbeddings(model: string, corpus: string, embedding: number[], layer: number, heads: number[], k = 10, hashObj: {} | null = null): Promise<rsp.NearestNeighborResponse> {
145
- const toSend = {
146
- model: model,
147
- corpus: corpus,
148
- embedding: embedding,
149
- layer: layer,
150
- heads: heads,
151
- k: k,
152
- }
153
-
154
- const url = makeUrl(this.baseURL + '/k-nearest-embeddings', toSend);
155
- console.log("--- GET " + url);
156
-
157
- if (hashObj != null) {
158
- const key = hash.sha1(toSend)
159
- d3.json(url).then(r => {
160
- hashObj[key] = r;
161
- })
162
- }
163
-
164
- return checkDemoAPI(toSend, url)
165
- }
166
-
167
- getNearestContexts(model: string, corpus: string, context: number[], layer: number, heads: number[], k = 10, hashObj: {} | null = null): Promise<rsp.NearestNeighborResponse> {
168
- const toSend = {
169
- model: model,
170
- corpus: corpus,
171
- context: context,
172
- layer: layer,
173
- heads: heads,
174
- k: k,
175
- }
176
-
177
- const url = makeUrl(this.baseURL + '/k-nearest-contexts', toSend);
178
- console.log("--- GET " + url);
179
-
180
- if (hashObj != null) {
181
- const key = hash.sha1(toSend)
182
- d3.json(url).then(r => {
183
- hashObj[key] = r;
184
- })
185
- }
186
-
187
- return checkDemoAPI(toSend, url)
188
- }
189
- };
 
134
 
135
  return checkDemoAPI(toSend, url, payload)
136
  }
137
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/api/responses.ts CHANGED
@@ -16,8 +16,4 @@ export interface ModelDetailResponse extends BaseResponse {
16
 
17
  export interface AttentionDetailsResponse extends BaseResponse {
18
  payload: tp.AttentionResponse
19
- }
20
-
21
- export interface NearestNeighborResponse extends BaseResponse {
22
- payload: tp.FaissSearchResults[]
23
  }
 
16
 
17
  export interface AttentionDetailsResponse extends BaseResponse {
18
  payload: tp.AttentionResponse
 
 
 
 
19
  }
client/src/ts/data/FaissSearchWrapper.ts DELETED
@@ -1,127 +0,0 @@
1
- import * as tp from '../etc/types'
2
- import * as d3 from 'd3'
3
- import 'd3-array'
4
- import * as R from 'ramda'
5
- import {SpacyInfo} from '../etc/SpacyInfo'
6
- import {initZero} from '../etc/xramda'
7
-
8
- // If value is not a string, don't try to make lowercase
9
- const makeStringLower = R.ifElse(R.is(String), R.toLower, R.identity)
10
-
11
- function argMax(array:number[]) {
12
- return [].map.call(array, (x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
13
- }
14
-
15
-
16
- export class FaissSearchResultWrapper {
17
- data: tp.FaissSearchResults[]
18
-
19
- options = {
20
- showNext: false
21
- }
22
-
23
- constructor(data: tp.FaissSearchResults[], showNext=false) {
24
- this.data = data
25
- this.options.showNext = showNext
26
- }
27
-
28
- get matchAtt() {
29
- return this.showNext() ? "matched_att_plus_1" : "matched_att"
30
- }
31
-
32
- get matchIdx() {
33
- return this.showNext() ? "next_index" : "index"
34
- }
35
-
36
- /**
37
- * Add position info interpretable by the histogram
38
- *
39
- * @param countObj Represents the inforrmation to be displayed by the histogram
40
- */
41
- countPosInfo() {
42
- const attOffsets = this.data.map((d,i) => +d[this.matchAtt].out.offset_to_max)
43
-
44
- const ctObj = {
45
- offset: initZero(attOffsets)
46
- }
47
-
48
- attOffsets.forEach(v => {
49
- Object.keys(ctObj).forEach((k) => {
50
- ctObj[k][v] += 1
51
- })
52
- })
53
-
54
- return ctObj
55
- }
56
-
57
- countMaxAttKeys(indexOffset=0) {
58
- // The keys in the below object dictate what we count
59
- const countObj = {
60
- pos: initZero(SpacyInfo.TotalMetaOptions.pos),
61
- dep: initZero(SpacyInfo.TotalMetaOptions.dep),
62
- is_ent: initZero(SpacyInfo.TotalMetaOptions.is_ent),
63
- }
64
-
65
- // Confusing: Show MATCHED WORD attentions, but NEXT WORD distribution
66
- const getMaxToken = (d: tp.FaissSearchResults) => d.tokens[argMax(d.matched_att.out.att)]
67
-
68
- this.data.forEach((d, i) => {
69
- const maxMatch = getMaxToken(d)
70
-
71
- Object.keys(countObj).forEach(k => {
72
- const val = makeStringLower(String(maxMatch[k]))
73
- countObj[k][val] += 1;
74
- })
75
- })
76
-
77
- const newCountObj = Object.assign(countObj, this.countPosInfo())
78
- return newCountObj
79
- }
80
-
81
- countMatchedKeys(indexOffset=0) {
82
- // The keys in the below object dictate what we count
83
- const countObj = {
84
- pos: initZero(SpacyInfo.TotalMetaOptions.pos),
85
- dep: initZero(SpacyInfo.TotalMetaOptions.dep),
86
- is_ent: initZero(SpacyInfo.TotalMetaOptions.is_ent),
87
- }
88
-
89
- this.data.forEach(d => {
90
- // Confusing: Show MATCHED WORD attentions, but NEXT WORD distribution
91
- const match = d.tokens[d[this.matchIdx] + indexOffset]
92
-
93
- Object.keys(countObj).forEach(k => {
94
- const val = makeStringLower(String(match[k]))
95
- countObj[k][val] += 1;
96
- })
97
- })
98
-
99
- return countObj
100
- }
101
-
102
- getMatchedHistogram(indexOffset=0) {
103
- const totalHist = this.countMatchedKeys(indexOffset)
104
- const filterZeros = (val, key) => val != 0;
105
- const nonZero = R.map(R.pickBy(filterZeros), totalHist)
106
-
107
- return nonZero
108
- }
109
-
110
- getMaxAttHistogram() {
111
- // const totalHist = this.countPosInfo()
112
- const newHist = this.countMaxAttKeys()
113
- const filterZeros = (val, key) => val != 0;
114
- const nonZero = R.map(R.pickBy(filterZeros), newHist)
115
-
116
- return nonZero
117
- }
118
-
119
- showNext(): boolean
120
- showNext(v:boolean): this
121
- showNext(v?) {
122
- if (v == null) return this.options.showNext
123
-
124
- this.options.showNext = v
125
- return this
126
- }
127
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/data/TokenWrapper.ts CHANGED
@@ -8,12 +8,6 @@ import * as R from 'ramda'
8
  */
9
  const emptyFullResponse: tp.FullSingleTokenInfo[] = [{
10
  text: '[SEP]',
11
- embeddings: [],
12
- contexts: [],
13
- bpe_token: '',
14
- bpe_pos: '',
15
- bpe_dep: '',
16
- bpe_is_ent: null,
17
  topk_words: [],
18
  topk_probs: []
19
  }]
 
8
  */
9
  const emptyFullResponse: tp.FullSingleTokenInfo[] = [{
10
  text: '[SEP]',
 
 
 
 
 
 
11
  topk_words: [],
12
  topk_probs: []
13
  }]
client/src/ts/etc/SpacyInfo.ts DELETED
@@ -1,61 +0,0 @@
1
- import * as tp from './types'
2
- import * as d3 from 'd3'
3
- import * as R from 'ramda'
4
- import {COLORS200} from '../etc/colors'
5
-
6
- export class SpacyInfo {
7
- colorScale:tp.ColorMetaScale
8
-
9
- constructor(){
10
- this.colorScale = this.createColorScales();
11
- }
12
-
13
- static EnglishMetaOptions: tp.MetaOptions = {
14
- pos: ['punct', 'sym', 'x', 'adj', 'verb', 'conj', 'num', 'et', 'adv', 'x', 'adp', 'noun', 'propn', 'part', 'pron', 'space', 'intj'],
15
- dep: ['root', 'ROOT', 'acl', 'acomp', 'advcl', 'advmod', 'agent', 'amod', 'appos', 'attr', 'aux', 'auxpass', 'case', 'cc', 'ccomp', 'compound', 'conj', 'cop', 'csubj',
16
- 'csubjpass', 'dative', 'dep', 'det', 'dobj', 'expl', 'intj', 'mark', 'meta', 'neg', 'nn', 'nounmod', 'npmod', 'nsubj', 'nsubjpass', 'nummod', 'oprd',
17
- 'obj', 'obl', 'parataxis', 'pcomp', 'pobj', 'poss', 'preconj', 'predet', 'prep', 'prt', 'punct', 'quantmod', 'relcl', 'root', 'xcomp', 'npadvmod'],
18
- is_ent: [true, false],
19
- ents: ['person', 'norp', 'fac', 'org', 'gpe', 'loc', 'product', 'event', 'work_of_art', 'law', 'language', 'date', 'time', 'percent', 'money', 'quantity', 'ordinal',
20
- 'cardinal'],
21
- }
22
-
23
- /**
24
- * Obsolete. Represents the information that is included when trained on the universal corpus
25
- */
26
- static UniversalMetaOptions: tp.MetaOptions = {
27
- pos: ['adj', 'adp', 'adv', 'aux', 'conj', 'cconj', 'det', 'intj', 'noun', 'num', 'part', 'pron', 'propn', 'punct', 'sconj', 'sym', 'verb', 'x', 'space'],
28
- dep: ['acl', 'advcl', 'advmod', 'amod', 'appos', 'aux', 'case', 'cc', 'ccomp', 'clf', 'compound', 'conj', 'cop', 'csubj', 'dep', 'det', 'discourse',
29
- 'dislocated', 'expl', 'fixed', 'flat', 'goeswith', 'iobj', 'list', 'mark', 'nmod', 'nsubj', 'nummod', 'obj', 'obl', 'orphan', 'parataxis', 'punct', 'reparandum',
30
- 'root', 'vocative', 'xcomp'],
31
- is_ent: [true, false],
32
- ents: ['person', 'norp', 'fac', 'org', 'gpe', 'loc', 'product', 'event', 'work_of_art', 'law', 'language', 'date', 'time', 'percent', 'money', 'quantity', 'ordinal',
33
- 'cardinal'],
34
- }
35
-
36
- static TotalMetaOptions: tp.MetaOptions = {
37
- pos: R.union(SpacyInfo.EnglishMetaOptions.pos, SpacyInfo.UniversalMetaOptions.pos),
38
- dep: SpacyInfo.EnglishMetaOptions.dep,
39
- is_ent: SpacyInfo.EnglishMetaOptions.is_ent,
40
- ents: SpacyInfo.EnglishMetaOptions.ents,
41
- }
42
-
43
- createColorScales(): tp.ColorMetaScale{
44
- const toScale = (keys: Array<number|string|boolean>) => {
45
- const obj = R.zipObj(R.map(String, keys), COLORS200.slice(0, keys.length))
46
- return k => R.propOr("black", k, obj)
47
- }
48
-
49
- const myColors = {
50
- pos: toScale(SpacyInfo.TotalMetaOptions.pos),
51
- dep: toScale(SpacyInfo.TotalMetaOptions.dep),
52
- is_ent: toScale(SpacyInfo.TotalMetaOptions.is_ent),
53
- ents: toScale(SpacyInfo.TotalMetaOptions.ents),
54
- offset: d3.scaleOrdinal().range(['black'])
55
- }
56
-
57
- return <tp.ColorMetaScale><unknown>myColors
58
- }
59
- }
60
-
61
- export const spacyColors = new SpacyInfo();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/etc/types.ts CHANGED
@@ -11,26 +11,21 @@ export type ModelInfo = {
11
  nheads: number
12
  }
13
 
14
- type AbstractAttentionResponse<T> = {
15
- aa: T
16
- }
17
 
18
  /**
19
  * ATTENTION RESULTS FROM BACKEND
20
  *
21
  * These are the results that are encased in the 'aa' and 'ab' keys returned
22
  */
 
 
 
 
23
  export type AttentionResponse = AbstractAttentionResponse<AttentionMetaResult>
24
  export type AttentionMetaResult = AbstractAttentionResult<FullSingleTokenInfo[]>
25
 
26
  export type FullSingleTokenInfo = {
27
  text: string,
28
- embeddings: number[],
29
- contexts: number[],
30
- bpe_token: string,
31
- bpe_pos: string,
32
- bpe_dep: string,
33
- bpe_is_ent: boolean,
34
  topk_words: string[],
35
  topk_probs: number[]
36
  }
@@ -57,28 +52,6 @@ interface MatchedAttentions {
57
  out: MatchedTokAtt,
58
  }
59
 
60
- export interface FaissSearchResults {
61
- sentence: string
62
- index: number
63
- next_index: number
64
- match: string
65
- match_plus_1: string
66
- matched_att: MatchedAttentions
67
- matched_att_plus_1: MatchedAttentions
68
- tokens: TokenFaissMatch[]
69
- }
70
-
71
- export interface TokenFaissMatch {
72
- token: string
73
- pos: string
74
- dep: string
75
- is_ent: string
76
- is_match: boolean
77
- is_next_word: boolean
78
- inward: number[]
79
- outward: number[]
80
- }
81
-
82
  /**
83
  * EVENT TYPES
84
  */
@@ -102,10 +75,7 @@ export type HeadBoxEvent = {
102
  * MISCELLANEOUS TYPES
103
  */
104
 
105
- export type SentenceOptions = "ab" | "ba" | "aa" | "bb" | "all";
106
  export type SideOptions = "left" | "right"
107
- export type SimpleMeta = "pos" | "dep" | "is_ent"
108
- export type TokenOptions = "a" | "b" | "all"
109
 
110
  export enum Toggled {
111
  ADDED = 0,
@@ -113,35 +83,14 @@ export enum Toggled {
113
  }
114
 
115
  export enum NormBy {
116
- Row = 0,
117
- Col,
118
- All
119
- }
120
-
121
- export interface AbstractMetaOptions {
122
- pos: string[],
123
- dep: string[],
124
- is_ent: any,
125
- ents: string[],
126
- }
127
-
128
- export interface MetaOptions extends AbstractMetaOptions {
129
- is_ent: boolean[],
130
- }
131
-
132
- export interface ColorMetaOptions extends AbstractMetaOptions {
133
- is_ent: string[] // Representing hex colors
134
- }
135
-
136
- export interface ColorMetaScale {
137
- pos: (d: string) => string,
138
- dep: (d: string) => string,
139
- is_ent: (d: string) => string,
140
- ents: (d: string) => string,
141
- offset?: (d: string) => string,
142
  }
143
 
144
  export enum ModelKind {
145
  Bidirectional = "bidirectional",
146
  Autoregressive = "autoregressive"
147
- }
 
 
 
11
  nheads: number
12
  }
13
 
 
 
 
14
 
15
  /**
16
  * ATTENTION RESULTS FROM BACKEND
17
  *
18
  * These are the results that are encased in the 'aa' and 'ab' keys returned
19
  */
20
+ type AbstractAttentionResponse<T> = {
21
+ aa: T
22
+ }
23
+
24
  export type AttentionResponse = AbstractAttentionResponse<AttentionMetaResult>
25
  export type AttentionMetaResult = AbstractAttentionResult<FullSingleTokenInfo[]>
26
 
27
  export type FullSingleTokenInfo = {
28
  text: string,
 
 
 
 
 
 
29
  topk_words: string[],
30
  topk_probs: number[]
31
  }
 
52
  out: MatchedTokAtt,
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  /**
56
  * EVENT TYPES
57
  */
 
75
  * MISCELLANEOUS TYPES
76
  */
77
 
 
78
  export type SideOptions = "left" | "right"
 
 
79
 
80
  export enum Toggled {
81
  ADDED = 0,
 
83
  }
84
 
85
  export enum NormBy {
86
+ ROW = 0,
87
+ COL,
88
+ ALL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  export enum ModelKind {
92
  Bidirectional = "bidirectional",
93
  Autoregressive = "autoregressive"
94
+ }
95
+ export type TokenOptions = "a" | "b" | "all"
96
+ export type SentenceOptions = "ab" | "ba" | "aa" | "bb" | "all";
client/src/ts/main.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { MainGraphic } from './vis/myMain'
 
2
  import { API, emptyTokenDisplay } from './api/mainApi'
3
  import * as _ from 'lodash'
4
  import { TokenWrapper } from './data/TokenWrapper'
@@ -8,112 +9,14 @@ import "!file-loader?name=exBERT.html!../exBERT.html";
8
  import "!file-loader?name=index.html!../index.html";
9
  import "../css/main.scss"
10
 
11
-
12
  function doMySvg() {
13
- return new MainGraphic()
 
14
  };
15
 
16
- /**
17
- * Create the static files needed for the demo. Save the keys and file paths to a json object that is then written to a file
18
- *
19
- * This will print the object after every call. When the key length is the expected length, right click in chrome and select "save as global variable"
20
- *
21
- * Then, in the console, type "copy(temp1)". Use sublime text (it is the best for handling large files) to paste this into the code and save it as ____.json
22
- *
23
- * @param sentence - The sentence to analyze
24
- * @param maskInd - Which index to mask in the sentence. Atm, can only record one masking
25
- * @param outDictPath - Where to save the object of hashkey: filepath
26
- */
27
- function createDemos(sentence, maskInd: number, modelName: string, corpusName: string, outDictPath) {
28
- const api = new API()
29
- const layers = _.range(12)
30
-
31
- const L = 0
32
-
33
- const contentHash = {} // Map hash -> contents
34
-
35
- // Get the base return for all page initializations
36
- _.range(12).forEach(L => {
37
- api.getMetaAttentions(modelName, sentence, L, contentHash).then(r0 => {
38
- const tokCapsule = new TokenWrapper(r0.payload);
39
-
40
- // Unmasked response:
41
- api.updateMaskedAttentions(modelName, tokCapsule.a, sentence, L, contentHash).then(r1 => {
42
- // Masked word and searching responses:
43
- tokCapsule.a.mask(maskInd)
44
- api.updateMaskedAttentions(modelName, tokCapsule.a, sentence, L, contentHash).then(r2 => {
45
- // Get search results by embedding
46
- const embedding = r2['aa']['left'][maskInd].embeddings
47
- api.getNearestEmbeddings(modelName, corpusName, embedding, L, _.range(12), 50, contentHash).then(x => {
48
- })
49
-
50
- // Get search results by context
51
- const context = r2['aa']['left'][maskInd].contexts
52
- api.getNearestContexts(modelName, corpusName, context, L, _.range(12), 50, contentHash).then(x => {
53
- console.log(Object.keys(contentHash).length);
54
- console.log(contentHash);
55
- })
56
- })
57
- })
58
- })
59
- })
60
- }
61
-
62
- /**
63
- *
64
- * Observe how the demo creation process works.
65
- *
66
- * If desired to mask multiple words in the input for demo purposes, try looping over the mask inds and masking each one individually
67
- *
68
- * @param sentence The demo sentence
69
- * @param maskInd Desired index to mask (can currently only accept a single mask index)
70
- * @param outDictPath
71
- */
72
- function inspectDemos(sentence, maskInd: number, modelName: string, corpusName: string, outDictPath) {
73
- const api = new API()
74
-
75
- const contentHash = {}
76
-
77
- // Get the base return for all page initializations
78
- _.range(1).forEach(L => {
79
- api.getMetaAttentions(modelName, sentence, L, "").then(r0 => {
80
- const tokCapsule = new TokenWrapper(r0.payload);
81
-
82
- // Unmasked response:
83
- api.updateMaskedAttentions(modelName, tokCapsule.a, sentence, L, emptyTokenDisplay).then(r1 => {
84
- // Masked word and searching responses:
85
- tokCapsule.a.mask(maskInd)
86
- api.updateMaskedAttentions(modelName, tokCapsule.a, sentence, L, emptyTokenDisplay).then(r2 => {
87
- console.log(r2);
88
- // Get search results by embedding
89
- const embedding = r2['aa']['left'][maskInd].embeddings
90
- api.getNearestEmbeddings(modelName, corpusName, embedding, L, _.range(12), 50, contentHash).then(x => {
91
- })
92
-
93
- // Get search results by context
94
- const context = r2['aa']['left'][maskInd].contexts
95
- api.getNearestContexts(modelName, corpusName, context, L, _.range(12), 50).then(x => {
96
- })
97
- })
98
- })
99
- })
100
- })
101
- }
102
-
103
- function replTest() {
104
- // Tester.testAttWrapperConstructor()
105
- // Tester.testUpdateMaskedAttention()
106
- // Tester.testNjAray();
107
- // Tester.testRandomArrayCreation();
108
- // Tester.testFaissWrapper();
109
- // Tester.testD3Ordinal();
110
- // Tester.testFaissSearchResultsHist();
111
- // Tester.testReadingJSON();
112
- }
113
-
114
  window.onload = () => {
115
- doMySvg();
116
- // replTest();
117
- // createDemos("Chicken tastes absolutely delicious if you know what you're doing", 4, "")
118
  console.log("Done loading window");
119
  }
 
1
+ import { MainGraphic } from './vis/attentionVis'
2
+ import * as d3 from 'd3'
3
  import { API, emptyTokenDisplay } from './api/mainApi'
4
  import * as _ from 'lodash'
5
  import { TokenWrapper } from './data/TokenWrapper'
 
9
  import "!file-loader?name=index.html!../index.html";
10
  import "../css/main.scss"
11
 
 
12
  function doMySvg() {
13
+ const base = document.getElementById('static-init')
14
+ return new MainGraphic(base)
15
  };
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  window.onload = () => {
18
+ const base = document.getElementById('attention-vis')
19
+ //@ts-ignore
20
+ const mainVis = new MainGraphic(base)
21
  console.log("Done loading window");
22
  }
client/src/ts/uiConfig.ts CHANGED
@@ -16,16 +16,11 @@ interface URLParameters {
16
  sentence?: string
17
  model?: string
18
  modelKind?: string
19
- corpus?: string
20
  layer?: number
21
  heads?: number[]
22
  threshold?: number
23
  tokenInd?: number | 'null'
24
  tokenSide?: tp.SideOptions
25
- metaMatch?: tp.SimpleMeta | null
26
- metaMax?: tp.SimpleMeta | null
27
- displayInspector?: InspectorOptions
28
- offsetIdxs?: number[]
29
  maskInds?: number[]
30
  hideClsSep?: boolean
31
  }
@@ -34,19 +29,23 @@ export class UIConfig {
34
 
35
  private _conf: URLParameters = {}
36
  private _headSet: Set<number>;
37
- attType: tp.SentenceOptions;
38
  _nHeads: number | null;
39
  _nLayers: number | null;
40
  private _token: tp.TokenEvent;
41
 
42
  constructor() {
43
- this._nHeads = 12; // How do I automate this?
44
  this._nLayers = null;
45
- this.attType = 'aa'; // Don't allow this to be modified by the user.
46
  this.fromURL()
47
  this.toURL(false)
48
  }
49
 
 
 
 
 
50
 
51
  fromURL() {
52
  const params = URLHandler.parameters
@@ -55,17 +54,12 @@ export class UIConfig {
55
  model: params['model'] || 'bert-base-cased',
56
  modelKind: params['modelKind'] || tp.ModelKind.Bidirectional,
57
  sentence: params['sentence'] || "The girl ran to a local pub to escape the din of her city.",
58
- corpus: params['corpus'] || 'woz',
59
  layer: params['layer'] || 1,
60
  heads: this._initHeads(params['heads']),
61
  threshold: params['threshold'] || 0.7,
62
  tokenInd: params['tokenInd'] || null,
63
  tokenSide: params['tokenSide'] || null,
64
  maskInds: params['maskInds'] || [9],
65
- metaMatch: params['metaMatch'] || "pos",
66
- metaMax: params['metaMax'] || "pos",
67
- displayInspector: params['displayInspector'] || null,
68
- offsetIdxs: this._initOffsetIdxs(params['offsetIdxs']),
69
  hideClsSep: truthy(params['hideClsSep']) || true,
70
  }
71
 
@@ -73,20 +67,6 @@ export class UIConfig {
73
 
74
  }
75
 
76
- toURL(updateHistory = false) {
77
- URLHandler.updateUrl(this._conf, updateHistory)
78
- }
79
-
80
- private _initOffsetIdxs(v: (string | number)[] | null) {
81
- if (v == null) {
82
- return [-1, 0, 1]
83
- }
84
- else {
85
- const numberArr = R.map(toNumber, v);
86
- return numberArr;
87
- }
88
- }
89
-
90
  private _initHeads(v: number[] | null) {
91
  if (v == null || v.length < 1) {
92
  this.selectAllHeads()
@@ -237,26 +217,6 @@ export class UIConfig {
237
  return this
238
  }
239
 
240
- metaMatch(): tp.SimpleMeta;
241
- metaMatch(val: tp.SimpleMeta): this;
242
- metaMatch(val?) {
243
- if (val == null) return this._conf.metaMax;
244
-
245
- this._conf.metaMax = val;
246
- this.toURL();
247
- return this;
248
- }
249
-
250
- metaMax(): tp.SimpleMeta;
251
- metaMax(val: tp.SimpleMeta): this;
252
- metaMax(val?) {
253
- if (val == null) return this._conf.metaMatch;
254
-
255
- this._conf.metaMatch = val;
256
- this.toURL();
257
- return this;
258
- }
259
-
260
  maskInds(): number[];
261
  maskInds(val: number[]): this;
262
  maskInds(val?) {
@@ -267,28 +227,6 @@ export class UIConfig {
267
  return this;
268
  }
269
 
270
- displayInspector(): InspectorOptions;
271
- displayInspector(val: InspectorOptions): this;
272
- displayInspector(val?) {
273
- if (val == null) return this._conf.displayInspector;
274
-
275
- this._conf.displayInspector = val;
276
- this.toURL();
277
- return this;
278
- }
279
-
280
- offsetIdxs(): number[];
281
- offsetIdxs(val: number[]): this;
282
- offsetIdxs(val?) {
283
- if (val == null) return this._conf.offsetIdxs;
284
-
285
- // convert to numbers
286
-
287
- this._conf.offsetIdxs = R.map(toNumber, val);
288
- this.toURL();
289
- return this;
290
- }
291
-
292
  hideClsSep(): boolean;
293
  hideClsSep(val: boolean): this;
294
  hideClsSep(val?) {
@@ -341,13 +279,4 @@ export class UIConfig {
341
  get matchHistogramDescription() {
342
  return this.modelKind() == tp.ModelKind.Autoregressive ? "Next" : "Matched"
343
  }
344
-
345
- corpus(): string;
346
- corpus(val: string): this;
347
- corpus(val?) {
348
- if (val == null) return this._conf.corpus
349
- this._conf.corpus = val
350
- this.toURL();
351
- return this
352
- }
353
  }
 
16
  sentence?: string
17
  model?: string
18
  modelKind?: string
 
19
  layer?: number
20
  heads?: number[]
21
  threshold?: number
22
  tokenInd?: number | 'null'
23
  tokenSide?: tp.SideOptions
 
 
 
 
24
  maskInds?: number[]
25
  hideClsSep?: boolean
26
  }
 
29
 
30
  private _conf: URLParameters = {}
31
  private _headSet: Set<number>;
32
+ attType: "aa"
33
  _nHeads: number | null;
34
  _nLayers: number | null;
35
  private _token: tp.TokenEvent;
36
 
37
  constructor() {
38
+ this._nHeads = 12;
39
  this._nLayers = null;
40
+ this.attType = 'aa'
41
  this.fromURL()
42
  this.toURL(false)
43
  }
44
 
45
+ toURL(updateHistory = false) {
46
+ URLHandler.updateUrl(this._conf, updateHistory)
47
+ }
48
+
49
 
50
  fromURL() {
51
  const params = URLHandler.parameters
 
54
  model: params['model'] || 'bert-base-cased',
55
  modelKind: params['modelKind'] || tp.ModelKind.Bidirectional,
56
  sentence: params['sentence'] || "The girl ran to a local pub to escape the din of her city.",
 
57
  layer: params['layer'] || 1,
58
  heads: this._initHeads(params['heads']),
59
  threshold: params['threshold'] || 0.7,
60
  tokenInd: params['tokenInd'] || null,
61
  tokenSide: params['tokenSide'] || null,
62
  maskInds: params['maskInds'] || [9],
 
 
 
 
63
  hideClsSep: truthy(params['hideClsSep']) || true,
64
  }
65
 
 
67
 
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  private _initHeads(v: number[] | null) {
71
  if (v == null || v.length < 1) {
72
  this.selectAllHeads()
 
217
  return this
218
  }
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  maskInds(): number[];
221
  maskInds(val: number[]): this;
222
  maskInds(val?) {
 
227
  return this;
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  hideClsSep(): boolean;
231
  hideClsSep(val: boolean): this;
232
  hideClsSep(val?) {
 
279
  get matchHistogramDescription() {
280
  return this.modelKind() == tp.ModelKind.Autoregressive ? "Next" : "Matched"
281
  }
 
 
 
 
 
 
 
 
 
282
  }
client/src/ts/vis/AttentionConnector.ts CHANGED
@@ -61,11 +61,11 @@ export class AttentionGraph extends VComponent<AttentionData>{
61
  // Define whether to use the 'j' or 'i' attribute to calculate opacities
62
  private scaleIdx(): "i" | "j" {
63
  switch (this.normBy) {
64
- case tp.NormBy.Col:
65
  return 'j'
66
- case tp.NormBy.Row:
67
  return 'i'
68
- case tp.NormBy.All:
69
  return 'i'
70
 
71
  }
@@ -165,7 +165,7 @@ export class AttentionGraph extends VComponent<AttentionData>{
165
 
166
  // Group normalization
167
  switch (this.normBy){
168
- case tp.NormBy.Row:
169
  arr = this.edgeData.extent(1);
170
  this.opacityScales = [];
171
  arr.forEach((v, i) => {
@@ -176,7 +176,7 @@ export class AttentionGraph extends VComponent<AttentionData>{
176
  )
177
  })
178
  break;
179
- case tp.NormBy.Col:
180
  arr = this.edgeData.extent(0);
181
  this.opacityScales = [];
182
  arr.forEach((v, i) => {
@@ -187,7 +187,7 @@ export class AttentionGraph extends VComponent<AttentionData>{
187
  )
188
  })
189
  break;
190
- case tp.NormBy.All:
191
  const maxIn = d3.max(this.plotData.map((d) => d.v))
192
  for (let i = 0; i < this._data.length; i++) {
193
  this.opacityScales.push(d3.scaleLinear()
 
61
  // Define whether to use the 'j' or 'i' attribute to calculate opacities
62
  private scaleIdx(): "i" | "j" {
63
  switch (this.normBy) {
64
+ case tp.NormBy.COL:
65
  return 'j'
66
+ case tp.NormBy.ROW:
67
  return 'i'
68
+ case tp.NormBy.ALL:
69
  return 'i'
70
 
71
  }
 
165
 
166
  // Group normalization
167
  switch (this.normBy){
168
+ case tp.NormBy.ROW:
169
  arr = this.edgeData.extent(1);
170
  this.opacityScales = [];
171
  arr.forEach((v, i) => {
 
176
  )
177
  })
178
  break;
179
+ case tp.NormBy.COL:
180
  arr = this.edgeData.extent(0);
181
  this.opacityScales = [];
182
  arr.forEach((v, i) => {
 
187
  )
188
  })
189
  break;
190
+ case tp.NormBy.ALL:
191
  const maxIn = d3.max(this.plotData.map((d) => d.v))
192
  for (let i = 0; i < this._data.length; i++) {
193
  this.opacityScales.push(d3.scaleLinear()
client/src/ts/vis/CorpusHistogram.ts DELETED
@@ -1,253 +0,0 @@
1
- import {VComponent} from './VisComponent'
2
- import {spacyColors} from '../etc/SpacyInfo'
3
- import {SVG} from '../etc/SVGplus'
4
- import * as d3 from 'd3'
5
- import * as R from 'ramda'
6
- import { D3Sel } from '../etc/Util';
7
- import { SimpleEventHandler } from '../etc/SimpleEventHandler';
8
-
9
- interface MarginInfo {
10
- top: number,
11
- bottom: number,
12
- right: number,
13
- left: number
14
- }
15
-
16
- // Dependent on the options in the response
17
- type MatchedMetaSelections = "pos" | "dep" | "ent"
18
-
19
- interface MatchedMetaCount {
20
- pos: number
21
- dep: number
22
- is_ent: number
23
- }
24
-
25
- interface MaxAttMetaCount {
26
- offset: number
27
- }
28
-
29
- type MatchedDataInterface = MatchedMetaCount
30
- type MaxAttDataInterface = MaxAttMetaCount
31
- type DataInterface = MatchedDataInterface | MaxAttDataInterface
32
-
33
- interface CountedHist {
34
- label: string,
35
- count: number
36
- }
37
-
38
- type RenderDataInterface = CountedHist[]
39
-
40
-
41
- /**
42
- * Data formatting functions
43
- */
44
- const toRenderData = (obj: {[s: string]: number}): RenderDataInterface => Object.keys(obj).map((k, i) => {
45
- return {label: k, count: obj[k]}
46
- })
47
-
48
- const toStringOrNum = (a:string) => {
49
- const na = +a
50
- if (isNaN(na)) {
51
- return a
52
- }
53
- return na
54
- }
55
-
56
- const sortByLabel = R.sortBy(R.compose(toStringOrNum, R.prop('label')))
57
- const sortByCount = R.sortBy(R.prop('count'))
58
-
59
- const toOrderedRender = R.compose(
60
- R.reverse,
61
- // @ts-ignore -- TODO: fix
62
- sortByCount,
63
- toRenderData
64
- )
65
-
66
- export class CorpusHistogram<T> extends VComponent<T> {
67
-
68
- css_name = ''
69
-
70
- static events = {}
71
-
72
- _current = {
73
- chart: {
74
- height: null,
75
- width: null
76
- }
77
- }
78
-
79
- // D3 COMPONENTS
80
- svg: D3Sel
81
-
82
- options: {
83
- margin: MarginInfo
84
- barWidth: number
85
- width: number
86
- height: number
87
- val: string
88
- xLabelRot: number
89
- xLabelOffset: number
90
- yLabelOffset: number
91
- }
92
-
93
- axes = {
94
- x: d3.scaleBand(),
95
- y: d3.scaleLinear(),
96
- }
97
-
98
-
99
- constructor(d3parent: D3Sel, eventHandler?: SimpleEventHandler, options={}) {
100
- super(d3parent, eventHandler)
101
- this.options = {
102
- margin: {
103
- top: 10,
104
- right: 30,
105
- bottom: 50,
106
- left: 40
107
- },
108
- barWidth: 25,
109
- width: 185,
110
- height: 230,
111
- val: "pos", // Change Default, pass through constructor
112
- xLabelRot: 45,
113
- xLabelOffset: 15,
114
- yLabelOffset: 5,
115
-
116
- }
117
- this.superInitSVG()
118
- }
119
-
120
- meta():MatchedMetaSelections
121
- meta(val:MatchedMetaSelections): this
122
- meta(val?) {
123
- if (val == null) {
124
- return this.options.val;
125
- }
126
-
127
- this.options.val = val;
128
- this.update(this._data)
129
-
130
- return this;
131
- }
132
-
133
- _init() {}
134
-
135
- private createXAxis() {
136
- const self = this;
137
- const op = this.options;
138
- const width = op.width - op.margin.left - op.margin.right
139
-
140
- this.axes.x
141
- .domain(R.map(R.prop('label'), self.renderData))
142
- .rangeRound([0, width])
143
- .padding(0.1)
144
-
145
- this._current.chart.width = width;
146
- }
147
-
148
- private createYAxis() {
149
- const self = this;
150
- const op = this.options;
151
- const height = op.height - op.margin.top - op.margin.bottom
152
-
153
- this.axes.y
154
- .domain([0, +d3.max(R.map(R.prop('count'), self.renderData))])
155
- .rangeRound([height, 0])
156
-
157
- this._current.chart.height = height;
158
- }
159
-
160
- private createAxes() {
161
- this.createXAxis()
162
- this.createYAxis()
163
- }
164
-
165
- _wrangle(data: DataInterface) {
166
- const out = data[this.options.val]
167
- return toOrderedRender(out)
168
- }
169
-
170
- width():number
171
- width(val:number):this
172
- width(val?) {
173
- if (val == null) {
174
- return this.options.width;
175
- }
176
- this.options.width = val;
177
- this.updateWidth();
178
- this.createXAxis();
179
- return this;
180
- }
181
-
182
- height():number
183
- height(val:number):this
184
- height(val?) {
185
- if (val == null) {
186
- return this.options.height;
187
- }
188
-
189
- this.options.height = val;
190
- this.updateHeight();
191
- this.createYAxis();
192
- return this;
193
- }
194
-
195
- private updateWidth() {
196
- this.svg.attr('width', this.options.width)
197
- }
198
-
199
- private updateHeight() {
200
- this.svg.attr('height', this.options.height)
201
- }
202
-
203
- private figWidth(data: RenderDataInterface) {
204
- const op = this.options;
205
- return (data.length * op.barWidth) + op.margin.left + op.margin.right
206
- }
207
-
208
- _render(data:RenderDataInterface) {
209
- const self = this;
210
- const op = this.options;
211
- const curr = this._current;
212
-
213
- this.parent.html('')
214
- this.svg = this.parent
215
-
216
- this.createAxes();
217
- this.width(this.figWidth(data));
218
- this.updateHeight();
219
-
220
- // Initialize axes
221
- const g = self.svg.append("g")
222
- .attr("transform", SVG.translate({x: op.margin.left, y:op.margin.top}))
223
-
224
- // Hack to allow clearing this histograms to work
225
- self.base = g
226
-
227
- // Fix below for positional changing
228
- const axisBottom = g.append("g")
229
- .attr("transform", SVG.translate({x: 0, y:curr.chart.height}))
230
- .call(d3.axisBottom(self.axes.x))
231
-
232
- if (op.val != "offset") {
233
- axisBottom
234
- .selectAll("text")
235
- .attr("y", op.yLabelOffset) // Move below the axis
236
- .attr("x", op.xLabelOffset) // Offset to the right a bit
237
- .attr("transform", SVG.rotate(op.xLabelRot))
238
- }
239
-
240
- g.append("g")
241
- .call(d3.axisLeft(self.axes.y))
242
-
243
- g.selectAll(".bar")
244
- .data(data)
245
- .join('rect')
246
- .attr("class", "bar")
247
- .attr("x", function(d) { return self.axes.x(d.label); })
248
- .attr("y", function(d) { return self.axes.y(d.count); })
249
- .attr("width", self.axes.x.bandwidth())
250
- .attr("height", function(d) { return curr.chart.height - self.axes.y(d.count); })
251
- .style('fill', k => spacyColors.colorScale[op.val](k.label))
252
- }
253
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/vis/CorpusInspector.ts DELETED
@@ -1,150 +0,0 @@
1
- import * as d3 from "d3";
2
- import * as R from 'ramda'
3
- import 'd3-selection-multi'
4
- import {d3S, D3Sel} from "../etc/Util";
5
- import { VComponent } from "./VisComponent";
6
- import { SimpleEventHandler } from "../etc/SimpleEventHandler";
7
- import * as tp from "../etc/types"
8
- import '../etc/xd3'
9
-
10
- // Helpers
11
- const currMatchIdx = (elem) => +(<Element>elem.parentNode).getAttribute('matchidx')
12
- const currRowNum = (elem) => +(<Element>elem.parentNode).getAttribute('rownum')
13
- const backgroundColor = x => `rgba(128, 0, 150, ${0.6*x})`
14
-
15
- export class CorpusInspector extends VComponent<tp.FaissSearchResults[]>{
16
- css_name = 'corpus-inspector';
17
- _current: {};
18
-
19
- _data: tp.FaissSearchResults[]; // The passed data
20
-
21
- static events = {
22
- rowMouseOver: "CorpusInspector_rowMouseOver",
23
- rowMouseOut: "CorpusInspector_rowMouseOut",
24
- rowClick: "CorpusInspector_rowClick",
25
- rowDblClick: "CorpusInspector_rowDblClick",
26
- cellMouseOver: "CorpusInspector_cellMouseOver",
27
- cellMouseOut: "CorpusInspector_cellMouseOut",
28
- cellClick: "CorpusInspector_cellClick",
29
- cellDblClick: "CorpusInspector_cellDblClick",
30
- }
31
-
32
- options = {
33
- showNext: false
34
- }
35
-
36
- // COMPONENTS
37
- inspectorRows: D3Sel
38
- inspectorCells: D3Sel
39
- scaler = d3.scalePow().range([0,0.9]).exponent(2)
40
-
41
- constructor(d3Parent: D3Sel, eventHandler?:SimpleEventHandler, options: {} = {}) {
42
- super(d3Parent, eventHandler)
43
- this.superInitHTML(options)
44
- this._init()
45
- }
46
-
47
- private createRows() {
48
- const data = this._data
49
-
50
- this.inspectorRows = this.base.selectAll(".inspector-row")
51
- .data(data)
52
- .join('div')
53
- .classed('inspector-row', true)
54
- .attrs({
55
- matchIdx: d => d.index,
56
- rowNum: (d, i) => i,
57
- })
58
- .on("mouseover", (d, i) => {
59
- this.eventHandler.trigger(CorpusInspector.events.rowMouseOver, {})
60
- })
61
- }
62
-
63
- private addTooltip() {
64
- this.inspectorCells = this.inspectorCells
65
- .classed('celltooltip', true)
66
- .append('span')
67
- .classed('tooltiptext', true)
68
- .html((d, i, n) => {
69
- const entityStr = d.is_ent ? "<br>Entity" : ""
70
- const att = (<Element>n[i].parentNode).getAttribute('att').slice(0, 7)
71
- const attStr = `<br>Attention: ${att}`
72
-
73
- return `POS: ${d.pos.toLowerCase()}<br>DEP: ${d.dep.toLowerCase()}` + entityStr + attStr
74
- })
75
- }
76
-
77
- private createCells() {
78
- const self = this
79
-
80
- this.inspectorCells = this.inspectorRows.selectAll('.inspector-cell')
81
- .data((d:tp.FaissSearchResults) => d.tokens)
82
- .join('div')
83
- .classed('inspector-cell', true)
84
- .attr('index-offset', (d, i, n:HTMLElement[]) => {
85
- const matchIdx = currMatchIdx(n[i])
86
- return i - matchIdx
87
- })
88
- .attrs({
89
- pos: d => d.pos.toLowerCase(),
90
- dep: d => d.dep.toLowerCase(),
91
- is_ent: d => d.is_ent
92
- })
93
- .text(d => d.token.replace("\u0120", " "))
94
- .classed('matched-cell', d => d.is_match)
95
- .classed('next-cell', function(d) {
96
- return self.showNext() && d.is_next_word
97
- })
98
- .classed('gray-cell', function(d, i) {
99
- const idx = +currMatchIdx(this)
100
- return self.showNext() && i > idx
101
- })
102
-
103
- // Highlight the cells appropriately
104
- this.inspectorCells.each((d,i,n) => {
105
- const idx = currMatchIdx(n[i])
106
- if (i == idx) {
107
- const att = d.inward
108
- const maxAtt = +d3.max(att)
109
- const currRow = currRowNum(n[i])
110
- const scaler = self.scaler.domain([0, maxAtt])
111
-
112
- d3.selectAll(`.inspector-row[rownum='${currRow}']`)
113
- .selectAll(`.inspector-cell`)
114
- .style('background', (d, i) => {
115
- return backgroundColor(scaler(att[i]))
116
- })
117
- .attr('att', (d, i) => att[i])
118
- }
119
- })
120
-
121
- self.addTooltip()
122
- }
123
-
124
- private updateData() {
125
- this.createRows()
126
- this.createCells()
127
- }
128
-
129
- _init() {}
130
-
131
- _wrangle(data: tp.FaissSearchResults[]) {
132
- this._data = data
133
- return data;
134
- }
135
-
136
- _render(data: tp.FaissSearchResults[]) {
137
- // Remember that this._data is defined in wrangle which should always be called before render
138
- // as is defined in the update function
139
- this.updateData()
140
- }
141
-
142
- showNext(): boolean
143
- showNext(v:boolean): this
144
- showNext(v?) {
145
- if (v == null) return this.options.showNext
146
-
147
- this.options.showNext = v
148
- return this
149
- }
150
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/vis/CorpusMatManager.ts DELETED
@@ -1,321 +0,0 @@
1
- import * as d3 from 'd3'
2
- import * as R from 'ramda'
3
- import * as tp from '../etc/types'
4
- import { D3Sel } from '../etc/Util'
5
- import { VComponent } from '../vis/VisComponent'
6
- import { SimpleEventHandler } from "../etc/SimpleEventHandler";
7
- import { SVG } from "../etc/SVGplus"
8
- import { spacyColors } from "../etc/SpacyInfo"
9
- import "../etc/xd3"
10
-
11
- // Need additoinal height information to render boxes
12
- interface BaseDataInterface extends tp.FaissSearchResults {
13
- height: number
14
- }
15
- type DataInterface = BaseDataInterface[]
16
-
17
- interface ColorMetaBaseData {
18
- pos: string
19
- dep: string
20
- is_ent: boolean
21
- token: string
22
- }
23
-
24
- type DisplayOptions = "pos" | "dep" | "ent"
25
-
26
- function managerData2MatData(dataIn: DataInterface, indexOffset = 0, toPick = ['pos']) {
27
-
28
- const outOfRangeObj: ColorMetaBaseData = {
29
- pos: null,
30
- dep: null,
31
- is_ent: null,
32
- token: null,
33
- }
34
-
35
- const chooseProps = R.pick(toPick)
36
-
37
- const dataOut = dataIn.map(d => {
38
- const wordIdx = d.index + indexOffset;
39
- if ((wordIdx < 0) || (wordIdx >= d.tokens.length)) {
40
- return R.assoc('height', d.height, outOfRangeObj)
41
- }
42
-
43
- const newObj = chooseProps(d.tokens[wordIdx])
44
-
45
- return R.assoc('height', d.height, newObj)
46
- })
47
-
48
- return dataOut
49
- }
50
-
51
-
52
- export class CorpusMatManager extends VComponent<DataInterface>{
53
- css_name = 'corpus-mat-container'
54
- options = {
55
- cellWidth: 10,
56
- toPick: ['pos'],
57
- idxs: [-1, 0, 1],
58
- divHover: {
59
- width: 60,
60
- height: 40
61
- }
62
- }
63
-
64
- static events = {
65
- mouseOver: "CorpusMatManager_MouseOver",
66
- mouseOut: "CorpusMatManager_MouseOut",
67
- click: "CorpusMatManager_Click",
68
- dblClick: "CorpusMatManager_DblClick",
69
- rectMouseOver: "CorpusMatManager_RectMouseOver",
70
- rectMouseOut: "CorpusMatManager_RectMouseOut",
71
- rectClick: "CorpusMatManager_RectClick",
72
- rectDblClick: "CorpusMatManager_RectDblClick",
73
- }
74
-
75
- // The d3 components that are saved to make rendering faster
76
- corpusMats: D3Sel
77
- rowGroups: D3Sel
78
- divHover: D3Sel
79
-
80
- _current = {}
81
- rowCssName = 'index-match-results'
82
- cellCssName = 'index-cell-result'
83
-
84
- _data: DataInterface
85
-
86
- static colorScale: tp.ColorMetaScale = spacyColors.colorScale;
87
-
88
- // Selections
89
- constructor(d3parent: D3Sel, eventHandler?: SimpleEventHandler, options = {}) {
90
- super(d3parent, eventHandler)
91
- this.idxs = [-1, 0, 1];
92
- this.superInitHTML(options)
93
- this._init()
94
- }
95
-
96
- get idxs() {
97
- return this.options.idxs;
98
- }
99
-
100
- set idxs(val: number[]) {
101
- this.options.idxs = val
102
- }
103
-
104
- // Create static dom elements
105
- _init() {
106
- const self = this;
107
- this.corpusMats = this.base.selectAll('.corpus-mat')
108
- this.rowGroups = this.corpusMats.selectAll(`.${this.rowCssName}`)
109
- this.divHover = this.base.append('div')
110
- .classed('mat-hover-display', true)
111
- .classed('text-center', true)
112
- .style('width', String(this.options.divHover.width) + 'px')
113
- .style('height', String(this.options.divHover.height) + 'px')
114
-
115
- this.divHover.append('p')
116
- }
117
-
118
- pick(val: DisplayOptions) {
119
- this.options.toPick = [val]
120
- this.redraw()
121
- }
122
-
123
- addRight() {
124
- const addedIdx = R.last(this.idxs) + 1;
125
- this.idxs.push(addedIdx)
126
- this.addCorpusMat(addedIdx, "right")
127
- }
128
-
129
- addLeft() {
130
- const addedIdx = this.idxs[0] - 1;
131
- const addDecrementedHead: (x: number[]) => number[] = x => R.insert(0, R.head(x) - 1)(x)
132
- this.idxs = addDecrementedHead(this.idxs)
133
- this.addCorpusMat(addedIdx, "left")
134
- }
135
-
136
- killRight() {
137
- this.kill(Math.max(...this.idxs))
138
- }
139
-
140
- killLeft() {
141
- this.kill(Math.min(...this.idxs))
142
- }
143
-
144
- /**
145
- * Remove edge value from contained indexes
146
- *
147
- * @param d Index to remove
148
- */
149
- kill(d: number) {
150
- if (d != 0) {
151
- if (d == Math.min(...this.idxs) || d == Math.max(...this.idxs)) {
152
- this.idxs = R.without([d], this.idxs)
153
- this.base.selectAll(`.offset-${d}`).remove()
154
- }
155
- }
156
- }
157
-
158
- _wrangle(data: DataInterface) {
159
- return data
160
- }
161
-
162
- data(val?: DataInterface) {
163
- if (val == null) {
164
- return this._data;
165
- }
166
-
167
- this._data = val;
168
- this._updateData();
169
- return this;
170
- }
171
-
172
- /**
173
- * The main rendering code, called whenever the data changes.
174
- */
175
- private _updateData() {
176
- const self = this;
177
- const op = this.options;
178
-
179
- this.base.selectAll('.corpus-mat').remove()
180
-
181
- this.idxs.forEach((idxOffset, i) => {
182
- self.addCorpusMat(idxOffset)
183
- })
184
- }
185
-
186
- /**
187
- * Add another word's meta information matrix column to either side of the index
188
- *
189
- * @param idxOffset Distance of word from matched word in the sentence
190
- * @param toThe Indicates adding to the "left" or to the "right" of the index
191
- */
192
- addCorpusMat(idxOffset: number, toThe: "right" | "left" = "right") {
193
- const self = this;
194
- const op = this.options;
195
- const boxWidth = op.cellWidth * op.toPick.length;
196
- const boxHeight = R.sum(R.map(R.prop('height'), this._data))
197
-
198
- let corpusMat;
199
-
200
- if (toThe == "right") {
201
- corpusMat = this.base.append('div')
202
- }
203
- else if (toThe == "left") {
204
- corpusMat = this.base.insert('div', ":first-child")
205
- }
206
- else {
207
- throw Error("toThe must have argument of 'left' or 'right'")
208
- }
209
-
210
- corpusMat = corpusMat
211
- .data([idxOffset])
212
- .attr('class', `corpus-mat offset-${idxOffset}`)
213
- .attr('offset', idxOffset)
214
- .append('svg')
215
- .attrs({
216
- width: boxWidth,
217
- height: boxHeight,
218
- })
219
- .on('mouseover', function (d, i) {
220
- self.eventHandler.trigger(CorpusMatManager.events.mouseOver, { idx: i, offset: d, val: self.options.toPick[0] })
221
- })
222
- .on('mouseout', (d, i) => {
223
- this.eventHandler.trigger(CorpusMatManager.events.mouseOut, { idx: i, offset: d })
224
- })
225
-
226
- this.addRowGroup(corpusMat)
227
- }
228
-
229
- /**
230
- *
231
- * @param mat The base div on which to add matrices and rows
232
- */
233
- addRowGroup(mat: D3Sel) {
234
- const self = this;
235
- const op = this.options;
236
-
237
- const heights = R.map(R.prop('height'), this._data)
238
-
239
- const [heightSum, rawHeightList] = R.mapAccum((x, y) => [R.add(x, y), R.add(x, y)], 0, heights)
240
- const fixList: (x: number[]) => number[] = R.compose(R.dropLast(1),
241
- // @ts-ignore
242
- R.prepend(0)
243
- )
244
- const heightList = fixList(rawHeightList)
245
-
246
- const rowGroup = mat.selectAll(`.${self.rowCssName}`)
247
- .data(d => managerData2MatData(self._data, d, op.toPick))
248
- .join("g")
249
- .attr("class", (d, i) => {
250
- return `${self.rowCssName} ${self.rowCssName}-${i}`
251
- })
252
- .attr("row-num", (d,i) => i)
253
- .attr("height", d => d.height)
254
- .attr("transform", (d, i) => {
255
- const out = SVG.translate({
256
- x: 0,
257
- y: heightList[i],
258
- })
259
- return out
260
- })
261
-
262
- op.toPick.forEach(prop => {
263
- self.addRect(rowGroup, 0, prop)
264
- })
265
- }
266
-
267
- addRect(g: D3Sel, xShift: number, prop: string) {
268
- const self = this
269
- const op = this.options
270
-
271
- const rects = g.append('rect')
272
- .attrs({
273
- width: op.cellWidth,
274
- height: d => d.height - 3,
275
- transform: (d, i) => {
276
- return SVG.translate({
277
- x: xShift,
278
- y: 1.5,
279
- })
280
- },
281
- })
282
- .style('fill', d => CorpusMatManager.colorScale[prop](d[prop]))
283
-
284
-
285
- const getBaseX = () => (<HTMLElement>self.base.node()).getBoundingClientRect().left
286
- const getBaseY = () => (<HTMLElement>self.base.node()).getBoundingClientRect().top
287
-
288
- g.on('mouseover', function (d, i) {
289
- self.divHover.style('visibility', 'visible')
290
- // Get offset
291
- const col = d3.select(this.parentNode.parentNode) // Column
292
- const offset = +col.attr('offset')
293
- self.eventHandler.trigger(CorpusMatManager.events.rectMouseOver, {idx: i, offset: offset})
294
- })
295
- .on('mouseout', function (d, i) {
296
- self.divHover.style('visibility', 'hidden')
297
- const col = d3.select(this.parentNode.parentNode) // Column
298
- const offset = +col.attr('offset')
299
- self.eventHandler.trigger(CorpusMatManager.events.rectMouseOut, {idx: i, offset: offset})
300
- })
301
- .on('mousemove', function(d, i) {
302
- const mouse = d3.mouse(self.base.node())
303
- const divOffset = [3, 3]
304
- const left = mouse[0] + getBaseX() - (op.divHover.width + divOffset[0])
305
- const top = mouse[1] + getBaseY() - (op.divHover.height + divOffset[1])
306
- self.divHover
307
- .style('left', String(left) + 'px')
308
- .style('top', String(top) + 'px')
309
- .selectAll('p')
310
- .text(d[prop])
311
- })
312
- }
313
-
314
- /**
315
- * @param data Data to display
316
- */
317
- _render(data: DataInterface) {
318
- this._updateData();
319
- }
320
-
321
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/src/ts/vis/{myMain.ts → attentionVis.ts} RENAMED
@@ -1,3 +1,7 @@
 
 
 
 
1
  import * as d3 from 'd3';
2
  import * as _ from "lodash"
3
  import * as R from 'ramda'
@@ -9,19 +13,13 @@ import { UIConfig } from '../uiConfig'
9
  import { TextTokens, LeftTextToken, RightTextToken } from './TextToken'
10
  import { AttentionHeadBox, getAttentionInfo } from './AttentionHeadBox'
11
  import { AttentionGraph } from './AttentionConnector'
12
- import { CorpusInspector } from './CorpusInspector'
13
  import { TokenWrapper, sideToLetter } from '../data/TokenWrapper'
14
  import { AttentionWrapper, makeFromMetaResponse } from '../data/AttentionCapsule'
15
  import { SimpleEventHandler } from '../etc/SimpleEventHandler'
16
- import { CorpusMatManager } from '../vis/CorpusMatManager'
17
- import { CorpusHistogram } from '../vis/CorpusHistogram'
18
- import { FaissSearchResultWrapper } from '../data/FaissSearchWrapper'
19
  import { D3Sel, Sel } from '../etc/Util';
20
- import { from, fromEvent, interval } from 'rxjs'
21
  import { switchMap, map, tap } from 'rxjs/operators'
22
  import { BaseType } from "d3";
23
- import { SimpleMeta } from "../etc/types";
24
- import ChangeEvent = JQuery.ChangeEvent;
25
 
26
 
27
  function isNullToken(tok: tp.TokenEvent) {
@@ -69,8 +67,199 @@ function setSelDisabled(attr: boolean, sel: D3Sel) {
69
  sel.attr('disabled', val)
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  export class MainGraphic {
 
74
  api: API
75
  uiConf: UIConfig
76
  attCapsule: AttentionWrapper
@@ -79,75 +268,17 @@ export class MainGraphic {
79
  vizs: any // Contains vis components wrapped around parent sel
80
  eventHandler: SimpleEventHandler // Orchestrates events raised from components
81
 
82
- constructor() {
83
- this.api = new API()
84
- this.uiConf = new UIConfig()
85
- this.skeletonInit()
86
- this.mainInit();
87
- }
88
-
89
  /**
90
- * Functions that can be called without any information of a response.
91
  *
92
- * This should be called once and only once
93
  */
94
- skeletonInit() {
95
- this.sels = {
96
- body: d3.select('body'),
97
- atnContainer: d3.select('#atn-container'),
98
- atnDisplay: d3.select("#atn-display"),
99
- modelSelector: d3.select("#model-option-selector"),
100
- corpusSelector: d3.select("#corpus-select"),
101
- atnHeads: {
102
- left: d3.select("#left-att-heads"),
103
- right: d3.select("#right-att-heads"),
104
- headInfo: d3.select("#head-info-box")
105
- .classed('mat-hover-display', true)
106
- .classed('text-center', true)
107
- .style('width', String(70) + 'px')
108
- .style('height', String(30) + 'px')
109
- .style('visibillity', 'hidden')
110
- },
111
- form: {
112
- sentenceA: d3.select("#form-sentence-a"),
113
- button: d3.select("#update-sentence"),
114
- },
115
- tokens: {
116
- left: d3.select("#left-tokens"),
117
- right: d3.select("#right-tokens"),
118
- },
119
- clsToggle: d3.select("#cls-toggle").select(".switch"),
120
- layerCheckboxes: d3.select("#layer-select"),
121
- headCheckboxes: d3.select("#head-select"),
122
- contextQuery: d3.select("#search-contexts"),
123
- embeddingQuery: d3.select("#search-embeddings"),
124
- selectedHeads: d3.select("#selected-heads"),
125
- headSelectAll: d3.select("#select-all-heads"),
126
- headSelectNone: d3.select("#select-no-heads"),
127
- testCheckbox: d3.select("#simple-embed-query"),
128
- threshSlider: d3.select("#my-range"),
129
- corpusInspector: d3.select("#corpus-similar-sentences-div"),
130
- corpusMatManager: d3.select("#corpus-mat-container"),
131
- corpusMsgBox: d3.select("#corpus-msg-box"),
132
- histograms: {
133
- matchedWordDescription: d3.select("#match-kind"),
134
- matchedWord: d3.select("#matched-histogram-container"),
135
- maxAtt: d3.select("#max-att-histogram-container"),
136
- },
137
- buttons: {
138
- killLeft: d3.select("#kill-left"),
139
- addLeft: d3.select("#minus-left"),
140
- addRight: d3.select("#plus-right"),
141
- killRight: d3.select("#kill-right"),
142
- refresh: d3.select("#mat-refresh")
143
- },
144
- metaSelector: {
145
- matchedWord: d3.select("#matched-meta-select"),
146
- maxAtt: d3.select("#max-att-meta-select")
147
- }
148
- }
149
 
150
- this.eventHandler = new SimpleEventHandler(<Element>this.sels.body.node());
151
 
152
  this.vizs = {
153
  leftHeads: new AttentionHeadBox(this.sels.atnHeads.left, this.eventHandler, { side: "left", }),
@@ -157,15 +288,11 @@ export class MainGraphic {
157
  right: new RightTextToken(this.sels.tokens.right, this.eventHandler),
158
  },
159
  attentionSvg: new AttentionGraph(this.sels.atnDisplay, this.eventHandler),
160
- corpusInspector: new CorpusInspector(this.sels.corpusInspector, this.eventHandler),
161
- corpusMatManager: new CorpusMatManager(this.sels.corpusMatManager, this.eventHandler, { idxs: this.uiConf.offsetIdxs() }),
162
- histograms: {
163
- matchedWord: new CorpusHistogram(this.sels.histograms.matchedWord, this.eventHandler),
164
- maxAtt: new CorpusHistogram(this.sels.histograms.maxAtt, this.eventHandler),
165
- },
166
  }
167
 
168
  this._bindEventHandler()
 
 
169
  }
170
 
171
  private mainInit() {
@@ -183,23 +310,14 @@ export class MainGraphic {
183
  // Wrap postInit into function so asynchronous call does not mess with necessary inits
184
  const postResponseDisplayCleanup = () => {
185
  this._toggleTokenSel()
186
-
187
- const toDisplay = this.uiConf.displayInspector()
188
- this._searchDisabler()
189
-
190
- if (toDisplay == 'context') {
191
- this._queryContext()
192
- } else if (toDisplay == 'embeddings') {
193
- this._queryEmbeddings()
194
- }
195
  }
196
 
197
  let normBy
198
  if ((this.uiConf.modelKind() == tp.ModelKind.Autoregressive) && (!this.uiConf.hideClsSep())) {
199
- normBy = tp.NormBy.Col
200
  }
201
  else {
202
- normBy = tp.NormBy.All
203
  }
204
  this.vizs.attentionSvg.normBy = normBy
205
 
@@ -363,35 +481,9 @@ export class MainGraphic {
363
  unselectHead(e.head)
364
  }
365
 
366
- this._searchDisabler()
367
  this._renderHeadSummary();
368
  this.renderSvg();
369
  })
370
-
371
- this.eventHandler.bind(CorpusMatManager.events.mouseOver, (e: { val: "pos" | "dep" | "is_ent", offset: number }) => {
372
- // Uncomment the below if you want to modify the whole column
373
- // const selector = `.inspector-cell[index-offset='${e.offset}']`
374
- // d3.selectAll(selector).classed("hovered-col", true)
375
- })
376
-
377
- this.eventHandler.bind(CorpusMatManager.events.mouseOut, (e: { offset: number, idx: number }) => {
378
- // Uncomment the below if you want to modify the whole column
379
- // const selector = `.inspector-cell[index-offset='${e.offset}']`
380
- // d3.selectAll(selector).classed("hovered-col", false)
381
- })
382
-
383
- this.eventHandler.bind(CorpusMatManager.events.rectMouseOver, (e: { offset: number, idx: number }) => {
384
- const row = d3.select(`.inspector-row[rownum='${e.idx}']`)
385
- const word = row.select(`.inspector-cell[index-offset='${e.offset}']`)
386
- word.classed("hovered-col", true)
387
- })
388
-
389
- this.eventHandler.bind(CorpusMatManager.events.rectMouseOut, (e: { offset: number, idx: number }) => {
390
- const row = d3.select(`.inspector-row[rownum='${e.idx}']`)
391
- const word = row.select(`.inspector-cell[index-offset='${e.offset}']`)
392
- word.classed("hovered-col", false)
393
- })
394
-
395
  }
396
 
397
  private _toggleTokenSel() {
@@ -421,8 +513,6 @@ export class MainGraphic {
421
  this.grayToggle(+e.ind)
422
  this.markNextToggle(+e.ind, this.tokCapsule.a.length())
423
  }
424
-
425
- this._searchDisabler()
426
  }
427
 
428
  /** Gray all tokens that have index greater than ind */
@@ -463,189 +553,15 @@ export class MainGraphic {
463
 
464
  }
465
 
466
- private _initModelSelection() {
467
- const self = this
468
-
469
- // Below are the available models. Will need to choose 3 to be available ONLY
470
- const data = [
471
- { name: "bert-base-cased", kind: tp.ModelKind.Bidirectional },
472
- { name: "bert-base-uncased", kind: tp.ModelKind.Bidirectional },
473
- { name: "distilbert-base-uncased", kind: tp.ModelKind.Bidirectional },
474
- { name: "distilroberta-base", kind: tp.ModelKind.Bidirectional },
475
- // { name: "roberta-base", kind: tp.ModelKind.Bidirectional },
476
- { name: "gpt2", kind: tp.ModelKind.Autoregressive },
477
- // { name: "gpt2-medium", kind: tp.ModelKind.Autoregressive },
478
- // { name: "distilgpt2", kind: tp.ModelKind.Autoregressive },
479
- ]
480
-
481
- const names = R.map(R.prop('name'))(data)
482
- const kinds = R.map(R.prop('kind'))(data)
483
- const kindmap = R.zipObj(names, kinds)
484
-
485
- this.sels.modelSelector.selectAll('.model-option')
486
- .data(data)
487
- .join('option')
488
- .classed('model-option', true)
489
- .property('value', d => d.name)
490
- .attr("modelkind", d => d.kind)
491
- .text(d => d.name)
492
-
493
- this.sels.modelSelector.property('value', this.uiConf.model());
494
-
495
- this.sels.modelSelector.on('change', function () {
496
- const me = d3.select(this)
497
- const mname = me.property('value')
498
- self.uiConf.model(mname);
499
- self.uiConf.modelKind(kindmap[mname]);
500
- if (kindmap[mname] == tp.ModelKind.Autoregressive) {
501
- console.log("RESETTING MASK INDS");
502
- self.uiConf.maskInds([])
503
- }
504
- self.mainInit();
505
- })
506
- }
507
-
508
- private _initCorpusSelection() {
509
- const data = [
510
- { code: "woz", display: "Wizard of Oz" },
511
- { code: "wiki", display: "Wikipedia" },
512
- ]
513
-
514
- const self = this
515
- self.sels.corpusSelector.selectAll('option')
516
- .data(data)
517
- .join('option')
518
- .property('value', d => d.code)
519
- .text(d => d.display)
520
-
521
- this.sels.corpusSelector.on('change', function () {
522
- const me = d3.select(this)
523
- self.uiConf.corpus(me.property('value'))
524
- console.log(self.uiConf.corpus());
525
- })
526
-
527
-
528
- }
529
-
530
  private _staticInits() {
531
  this._initSentenceForm();
532
  this._initModelSelection();
533
- this._initCorpusSelection();
534
- this._initQueryForm();
535
- this._initAdder();
536
  this._renderHeadSummary();
537
- this._initMetaSelectors();
538
  this._initToggle();
539
  this.renderAttHead();
540
  this.renderTokens();
541
  }
542
 
543
- private _initAdder() {
544
- const updateUrlOffsetIdxs = () => {
545
- this.uiConf.offsetIdxs(this.vizs.corpusMatManager.idxs)
546
- }
547
-
548
- const fixCorpusMatHeights = () => {
549
- const newWrapped = this._wrapResults(this.vizs.corpusMatManager.data())
550
- this.vizs.corpusMatManager.data(newWrapped.data)
551
- updateUrlOffsetIdxs()
552
- }
553
-
554
- this.sels.buttons.addRight.on('click', () => {
555
- this.vizs.corpusMatManager.addRight()
556
- updateUrlOffsetIdxs()
557
- })
558
-
559
- this.sels.buttons.addLeft.on('click', () => {
560
- this.vizs.corpusMatManager.addLeft()
561
- updateUrlOffsetIdxs()
562
- })
563
-
564
- this.sels.buttons.killRight.on('click', () => {
565
- this.vizs.corpusMatManager.killRight()
566
- updateUrlOffsetIdxs()
567
- })
568
-
569
- this.sels.buttons.killLeft.on('click', () => {
570
- this.vizs.corpusMatManager.killLeft()
571
- updateUrlOffsetIdxs()
572
- })
573
-
574
- this.sels.buttons.refresh.on('click', () => {
575
- fixCorpusMatHeights();
576
- })
577
-
578
- const onresize = () => {
579
- if (this.sels.corpusInspector.text() != '') fixCorpusMatHeights();
580
- }
581
-
582
- window.onresize = onresize
583
- }
584
-
585
- private _initMetaSelectors() {
586
- this._initMatchedWordSelector(this.sels.metaSelector.matchedWord)
587
- this._initMaxAttSelector(this.sels.metaSelector.maxAtt)
588
- }
589
-
590
- private _initMaxAttSelector(sel: D3Sel) {
591
- const self = this;
592
-
593
- const chooseSelected = (value) => {
594
- const ms = sel.selectAll('label')
595
- ms.classed('active', false)
596
- const el = sel.selectAll(`label[value=${value}]`)
597
- el.classed('active', true)
598
- }
599
-
600
- chooseSelected(this.uiConf.metaMax())
601
-
602
- const el = sel.selectAll('label')
603
- el.on('click', function () {
604
- const val = <SimpleMeta>d3.select(this).attr('value');
605
-
606
- // Do toggle
607
- sel.selectAll('.active').classed('active', false)
608
- d3.select(this).classed('active', true)
609
- self.uiConf.metaMax(val)
610
- self.vizs.histograms.maxAtt.meta(val)
611
- })
612
- }
613
-
614
- private _initMatchedWordSelector(sel: D3Sel) {
615
- const self = this;
616
-
617
- const chooseSelected = (value) => {
618
- const ms = sel.selectAll('label')
619
- ms.classed('active', false)
620
- const el = sel.selectAll(`label[value=${value}]`)
621
- el.classed('active', true)
622
- }
623
-
624
- chooseSelected(this.uiConf.metaMatch())
625
-
626
- const el = sel.selectAll('label')
627
- el.on('click', function () {
628
- const val = <SimpleMeta>d3.select(this).attr('value')
629
-
630
- // Do toggle
631
- sel.selectAll('.active').classed('active', false)
632
- d3.select(this).classed('active', true)
633
- self.uiConf.metaMatch(val)
634
- self._updateCorpusInspectorFromMeta(val)
635
- })
636
- }
637
-
638
- private _disableSearching(attr: boolean) {
639
- setSelDisabled(attr, this.sels.contextQuery)
640
- setSelDisabled(attr, this.sels.embeddingQuery)
641
- }
642
-
643
- private _updateCorpusInspectorFromMeta(val: tp.SimpleMeta) {
644
- this.vizs.corpusInspector.showNext(this.uiConf.showNext)
645
- this.vizs.corpusMatManager.pick(val)
646
- this.vizs.histograms.matchedWord.meta(val)
647
- }
648
-
649
  private _initSentenceForm() {
650
  const self = this;
651
 
@@ -697,165 +613,11 @@ export class MainGraphic {
697
  inputBox.on('keypress', onEnterSubmit)
698
  }
699
 
700
- private _getSearchEmbeds() {
701
- const savedToken = this.uiConf.token();
702
- const out = this.vizs.tokens[savedToken.side].getEmbedding(savedToken.ind)
703
- return out.embeddings
704
- }
705
-
706
- private _getSearchContext() {
707
- const savedToken = this.uiConf.token();
708
- const out = this.vizs.tokens[savedToken.side].getEmbedding(savedToken.ind)
709
- return out.contexts
710
- }
711
-
712
- private _searchEmbeddings() {
713
- const self = this;
714
- console.log("SEARCHING EMBEDDINGS");
715
- const embed = this._getSearchEmbeds()
716
- const layer = self.uiConf.layer()
717
- const heads = self.uiConf.heads()
718
- const k = 50
719
- self.vizs.corpusInspector.showNext(self.uiConf.showNext)
720
-
721
- this.sels.body.style("cursor", "progress")
722
- self.api.getNearestEmbeddings(self.uiConf.model(), self.uiConf.corpus(), embed, layer, heads, k)
723
- .then((val: rsp.NearestNeighborResponse) => {
724
- if (val.status == 406) {
725
- self.leaveCorpusMsg(`Embeddings are not available for model '${self.uiConf.model()}' and corpus '${self.uiConf.corpus()}' at this time.`)
726
- }
727
- else {
728
- const v = val.payload
729
-
730
- self.vizs.corpusInspector.unhideView()
731
- self.vizs.corpusMatManager.unhideView()
732
-
733
- // Get heights of corpus inspector rows.
734
- self.vizs.corpusInspector.update(v)
735
- const wrappedVals = self._wrapResults(v)
736
- const countedVals = wrappedVals.getMatchedHistogram()
737
- const offsetVals = wrappedVals.getMaxAttHistogram()
738
-
739
- self.vizs.corpusMatManager.update(wrappedVals.data)
740
- self.sels.histograms.matchedWordDescription.text(this.uiConf.matchHistogramDescription)
741
- console.log("MATCHER: ", self.sels.histograms.matchedWord);
742
- self.vizs.histograms.matchedWord.update(countedVals)
743
- self.vizs.histograms.maxAtt.update(offsetVals)
744
- self.uiConf.displayInspector('embeddings')
745
- this._updateCorpusInspectorFromMeta(this.uiConf.metaMatch())
746
- }
747
- this.sels.body.style("cursor", "default")
748
- })
749
- }
750
-
751
- private _searchContext() {
752
- const self = this;
753
- console.log("SEARCHING CONTEXTS");
754
- const context = this._getSearchContext()
755
- const layer = self.uiConf.layer()
756
- const heads = self.uiConf.heads()
757
- const k = 50
758
- self.vizs.corpusInspector.showNext(self.uiConf.showNext)
759
-
760
- this.sels.body.style("cursor", "progress")
761
-
762
- self.api.getNearestContexts(self.uiConf.model(), self.uiConf.corpus(), context, layer, heads, k)
763
- .then((val: rsp.NearestNeighborResponse) => {
764
- // Get heights of corpus inspector rows.
765
- if (val.status == 406) {
766
- console.log("Contexts are not available!");
767
- self.leaveCorpusMsg(`Contexts are not available for model '${self.uiConf.model()}' and corpus '${self.uiConf.corpus()}' at this time.`)
768
- }
769
- else {
770
- const v = val.payload;
771
- console.log("HIDING");
772
-
773
- self.vizs.corpusInspector.update(v)
774
-
775
- Sel.hideElement(self.sels.corpusMsgBox)
776
- self.vizs.corpusInspector.unhideView()
777
- self.vizs.corpusMatManager.unhideView()
778
-
779
- const wrappedVals = self._wrapResults(v)
780
- const countedVals = wrappedVals.getMatchedHistogram()
781
- const offsetVals = wrappedVals.getMaxAttHistogram()
782
- self.vizs.corpusMatManager.update(wrappedVals.data)
783
-
784
- self.vizs.histograms.matchedWord.update(countedVals)
785
- self.vizs.histograms.maxAtt.update(offsetVals)
786
-
787
- self.uiConf.displayInspector('context')
788
- this._updateCorpusInspectorFromMeta(this.uiConf.metaMatch())
789
- self.vizs.histograms.maxAtt.meta(self.uiConf.metaMax())
790
- }
791
- this.sels.body.style("cursor", "default")
792
- })
793
- }
794
-
795
- private _queryContext() {
796
- const self = this;
797
-
798
- if (this.uiConf.hasToken()) {
799
- this._searchContext();
800
- } else {
801
- console.log("Was told to show inspector but was not given a selected token embedding")
802
- }
803
- }
804
-
805
- private _queryEmbeddings() {
806
- const self = this;
807
-
808
- if (this.uiConf.hasToken()) {
809
- console.log("token: ", this.uiConf.token());
810
- this._searchEmbeddings();
811
- } else {
812
- console.log("Was told to show inspector but was not given a selected token embedding")
813
- }
814
- }
815
-
816
- private _searchingDisabled() {
817
- return (this.uiConf.heads().length == 0) || (!this.uiConf.hasToken())
818
- }
819
-
820
- private _searchDisabler() {
821
- this._disableSearching(this._searchingDisabled())
822
- }
823
-
824
- private _initQueryForm() {
825
- const self = this;
826
- this._searchDisabler()
827
-
828
- this.sels.contextQuery.on("click", () => {
829
- self._queryContext()
830
- })
831
-
832
- this.sels.embeddingQuery.on("click", () => {
833
- self._queryEmbeddings()
834
- })
835
- }
836
-
837
  private _renderHeadSummary() {
838
  this.sels.selectedHeads
839
  .html(R.join(', ', this.uiConf.heads().map(h => h + 1)))
840
  }
841
 
842
- // Modify faiss results with corresponding heights
843
- private _wrapResults(returnedFaissResults: tp.FaissSearchResults[]) {
844
-
845
- const rows = d3.selectAll('.inspector-row')
846
-
847
- // Don't just use offsetHeight since that rounds to the nearest integer
848
- const heights = rows.nodes().map((n: HTMLElement) => n.getBoundingClientRect().height)
849
-
850
- const newVals = returnedFaissResults.map((v, i) => {
851
- return R.assoc('height', heights[i], v)
852
- })
853
-
854
- const wrappedVals = new FaissSearchResultWrapper(newVals, this.uiConf.showNext)
855
-
856
- return wrappedVals
857
- }
858
-
859
  private initLayers(nLayers: number) {
860
  const self = this;
861
  let hasActive = false;
@@ -932,14 +694,12 @@ export class MainGraphic {
932
 
933
  this.sels.headSelectAll.on("click", function () {
934
  self.uiConf.selectAllHeads();
935
- self._searchDisabler()
936
  self.renderSvg()
937
  self.renderAttHead()
938
  })
939
 
940
  this.sels.headSelectNone.on("click", function () {
941
  self.uiConf.selectNoHeads();
942
- self._searchDisabler();
943
  self.renderSvg()
944
  self.renderAttHead()
945
  Sel.setHidden(".atn-curve")
@@ -961,6 +721,48 @@ export class MainGraphic {
961
  })
962
  }
963
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
964
  renderAttHead() {
965
  const heads = _.range(0, this.uiConf._nHeads)
966
  const focusAtt = this.attCapsule.att
@@ -1020,3 +822,5 @@ export class MainGraphic {
1020
  this.render();
1021
  }
1022
  }
 
 
 
1
+ /**
2
+ * Showing the top left part of exBERT, no information from the embeddings or the contexts
3
+ */
4
+
5
  import * as d3 from 'd3';
6
  import * as _ from "lodash"
7
  import * as R from 'ramda'
 
13
  import { TextTokens, LeftTextToken, RightTextToken } from './TextToken'
14
  import { AttentionHeadBox, getAttentionInfo } from './AttentionHeadBox'
15
  import { AttentionGraph } from './AttentionConnector'
 
16
  import { TokenWrapper, sideToLetter } from '../data/TokenWrapper'
17
  import { AttentionWrapper, makeFromMetaResponse } from '../data/AttentionCapsule'
18
  import { SimpleEventHandler } from '../etc/SimpleEventHandler'
 
 
 
19
  import { D3Sel, Sel } from '../etc/Util';
20
+ import { from, fromEvent } from 'rxjs'
21
  import { switchMap, map, tap } from 'rxjs/operators'
22
  import { BaseType } from "d3";
 
 
23
 
24
 
25
  function isNullToken(tok: tp.TokenEvent) {
 
67
  sel.attr('disabled', val)
68
  }
69
 
70
+ function createStaticSkeleton(base: D3Sel) {
71
+
72
+ /**
73
+ * Top level sections
74
+ */
75
+ const sentenceInput = base.append('div')
76
+ .attr("id", "sentence-input")
77
+
78
+ const connectorContainer = base.append('div')
79
+ .attr('id', 'connector-container')
80
+
81
+ const atnControls = connectorContainer.append('div')
82
+ .attr("id", "connector-controls")
83
+
84
+ const atnContainer = connectorContainer.append('div')
85
+ .attr("id", "atn-container")
86
+ .classed("text-center", true)
87
+
88
+ /**
89
+ * Sentence Input
90
+ */
91
+
92
+ const formGroup = sentenceInput.append('form')
93
+ .append('div').classed('form-group', true)
94
+
95
+ formGroup.append('label')
96
+ .attr('for', "form-sentence-a")
97
+ .text(' Input Sentence ')
98
+
99
+ const sentenceA = formGroup.append('input')
100
+ .attr('id', 'form-sentence-a')
101
+ .attr('type', 'text')
102
+ .attr('name', 'sent-a-input')
103
+
104
+ sentenceInput.append('div')
105
+ .classed('padding', true)
106
+
107
+ const formButton = sentenceInput.append('button')
108
+ .attr('class', 'btn btn-primary')
109
+ .attr('id', "update-sentence")
110
+ .attr('type', 'button')
111
+
112
+ formButton.text("Update")
113
+
114
+ /**
115
+ * Connector Controls
116
+ */
117
+ const leftControlHalf = atnControls.append('div')
118
+ .classed('left-control-half', true)
119
+
120
+ const rightControlHalf = atnControls.append('div')
121
+ .attr('class', 'right-control-half head-control')
122
+
123
+ const modelSelection = leftControlHalf.append('div')
124
+ .attr('id', 'model-selection')
125
+
126
+ modelSelection.append('label')
127
+ .attr('for', 'model-options').text('Select model')
128
+
129
+ const modelSelector = modelSelection.append('select')
130
+ .attr('id', 'model-option-selector')
131
+ .attr('name', 'model-options')
132
+
133
+ const slideContainer = leftControlHalf.append('div')
134
+ .classed('slide-container', true)
135
+
136
+ slideContainer.append('label')
137
+ .attr('for', 'my-range')
138
+ .html("Display top <span id=\"my-range-value\">...</span>% of attention")
139
+
140
+ const threshSlider = slideContainer.append('input')
141
+ .attr('type', 'range')
142
+ .attr('min', '0')
143
+ .attr('max', '100')
144
+ .attr('value', '70')
145
+ .classed('slider', true)
146
+ .attr('id', 'my-range')
147
+
148
+ const layerSelection = leftControlHalf.append('div')
149
+ .attr('id', 'layer-selection')
150
+
151
+ layerSelection.append('div')
152
+ .classed('input-description', true)
153
+ .text("Layer: ")
154
+
155
+ const layerCheckboxes = layerSelection.append('div')
156
+ .attr('class', 'layer-select btn-group btn-group-toggle')
157
+ .attr('data-toggle', 'buttons')
158
+ .attr('id', 'layer-select')
159
+
160
+ const clsToggle = leftControlHalf.append('div')
161
+ .attr('id', 'cls-toggle')
162
+
163
+ clsToggle.append('div')
164
+ .attr('class', 'input-description')
165
+ .text("Hide Special Tokens")
166
+
167
+ const clsSwitch = clsToggle.append('label')
168
+ .attr('class', 'switch')
169
+
170
+ clsSwitch.append('input').attr('type', 'checkbox')
171
+ .attr('checked', 'checked')
172
+
173
+ clsSwitch.append('span')
174
+ .attr('class', 'short-slider round')
175
+
176
+ const selectedHeads = rightControlHalf.append('div')
177
+ .attr('id', 'selected-head-display')
178
+
179
+ selectedHeads.append('div')
180
+ .classed('input-description', true)
181
+ .text('Selected heads:')
182
+
183
+ selectedHeads.append('div').attr('id', 'selected-heads')
184
+
185
+ const headButtons = rightControlHalf.append('div')
186
+ .classed('select-input', true)
187
+ .attr('id', 'head-all-or-none')
188
+
189
+ const headSelectAll = headButtons.append('button').attr('id', 'select-all-heads').text("Select all heads")
190
+ const headSelectNone = headButtons.append('button').attr('id', 'select-no-heads').text("Unselect all heads")
191
+
192
+ const infoContainer = rightControlHalf.append('div')
193
+ .attr('id', 'usage-info')
194
+
195
+ infoContainer.append('p').html("You focus on one token by <b>click</b>.<br /> You can mask any token by <b>double click</b>.")
196
+ infoContainer.append('p').html("You can select and de-select a head by a <b>click</b> on the heatmap columns")
197
+
198
+ connectorContainer.append('div').attr('id', 'vis-break')
199
+
200
+ /**
201
+ * For main attention vis
202
+ */
203
+
204
+ const headInfoBox = atnContainer.append('div')
205
+ .attr('id', "head-info-box")
206
+ .classed('mat-hover-display', true)
207
+ .classed('text-center', true)
208
+ .style('width', String(70) + 'px')
209
+ .style('height', String(30) + 'px')
210
+ .style('visibillity', 'hidden')
211
+
212
+ const headBoxLeft = atnContainer.append('svg')
213
+ .attr('id', 'left-att-heads')
214
+
215
+ const tokensLeft = atnContainer.append('div')
216
+ .attr("id", "left-tokens")
217
+
218
+ const atnDisplay = atnContainer.append('svg')
219
+ .attr("id", "atn-display")
220
+
221
+ const tokensRight = atnContainer.append('div')
222
+ .attr("id", "right-tokens")
223
+
224
+ const headBoxRight = atnContainer.append('svg')
225
+ .attr('id', 'right-att-heads')
226
+
227
+ /**
228
+ * Return an object that provides handles to the important parts here
229
+ */
230
+
231
+ const pctSpan = base.select("#my-range-value")
232
+
233
+ const sels = {
234
+ body: d3.select('body'),
235
+ atnContainer: atnContainer,
236
+ atnDisplay: atnDisplay,
237
+ atnHeads: {
238
+ left: headBoxLeft,
239
+ right: headBoxRight,
240
+ headInfo: headInfoBox
241
+ },
242
+ form: {
243
+ sentenceA: sentenceA,
244
+ button: formButton
245
+ },
246
+ tokens: {
247
+ left: tokensLeft,
248
+ right: tokensRight
249
+ },
250
+ modelSelector: modelSelector,
251
+ clsToggle: clsToggle,
252
+ layerCheckboxes: layerCheckboxes,
253
+ selectedHeads: selectedHeads,
254
+ headSelectAll: headSelectAll,
255
+ headSelectNone: headSelectNone,
256
+ threshSlider: threshSlider,
257
+ }
258
+ return sels
259
+ }
260
 
261
  export class MainGraphic {
262
+ base: D3Sel
263
  api: API
264
  uiConf: UIConfig
265
  attCapsule: AttentionWrapper
 
268
  vizs: any // Contains vis components wrapped around parent sel
269
  eventHandler: SimpleEventHandler // Orchestrates events raised from components
270
 
 
 
 
 
 
 
 
271
  /**
 
272
  *
273
+ * @param base 'div' html element into which everything below will be rendered
274
  */
275
+ constructor(baseDiv: Element) {
276
+ this.base = d3.select(baseDiv)
277
+ this.api = new API()
278
+ this.uiConf = new UIConfig()
279
+ this.sels = createStaticSkeleton(this.base)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
+ this.eventHandler = new SimpleEventHandler(<Element>this.base.node());
282
 
283
  this.vizs = {
284
  leftHeads: new AttentionHeadBox(this.sels.atnHeads.left, this.eventHandler, { side: "left", }),
 
288
  right: new RightTextToken(this.sels.tokens.right, this.eventHandler),
289
  },
290
  attentionSvg: new AttentionGraph(this.sels.atnDisplay, this.eventHandler),
 
 
 
 
 
 
291
  }
292
 
293
  this._bindEventHandler()
294
+
295
+ this.mainInit()
296
  }
297
 
298
  private mainInit() {
 
310
  // Wrap postInit into function so asynchronous call does not mess with necessary inits
311
  const postResponseDisplayCleanup = () => {
312
  this._toggleTokenSel()
 
 
 
 
 
 
 
 
 
313
  }
314
 
315
  let normBy
316
  if ((this.uiConf.modelKind() == tp.ModelKind.Autoregressive) && (!this.uiConf.hideClsSep())) {
317
+ normBy = tp.NormBy.COL
318
  }
319
  else {
320
+ normBy = tp.NormBy.ALL
321
  }
322
  this.vizs.attentionSvg.normBy = normBy
323
 
 
481
  unselectHead(e.head)
482
  }
483
 
 
484
  this._renderHeadSummary();
485
  this.renderSvg();
486
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  }
488
 
489
  private _toggleTokenSel() {
 
513
  this.grayToggle(+e.ind)
514
  this.markNextToggle(+e.ind, this.tokCapsule.a.length())
515
  }
 
 
516
  }
517
 
518
  /** Gray all tokens that have index greater than ind */
 
553
 
554
  }
555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
  private _staticInits() {
557
  this._initSentenceForm();
558
  this._initModelSelection();
 
 
 
559
  this._renderHeadSummary();
 
560
  this._initToggle();
561
  this.renderAttHead();
562
  this.renderTokens();
563
  }
564
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  private _initSentenceForm() {
566
  const self = this;
567
 
 
613
  inputBox.on('keypress', onEnterSubmit)
614
  }
615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
  private _renderHeadSummary() {
617
  this.sels.selectedHeads
618
  .html(R.join(', ', this.uiConf.heads().map(h => h + 1)))
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  private initLayers(nLayers: number) {
622
  const self = this;
623
  let hasActive = false;
 
694
 
695
  this.sels.headSelectAll.on("click", function () {
696
  self.uiConf.selectAllHeads();
 
697
  self.renderSvg()
698
  self.renderAttHead()
699
  })
700
 
701
  this.sels.headSelectNone.on("click", function () {
702
  self.uiConf.selectNoHeads();
 
703
  self.renderSvg()
704
  self.renderAttHead()
705
  Sel.setHidden(".atn-curve")
 
721
  })
722
  }
723
 
724
+ private _initModelSelection() {
725
+ const self = this
726
+
727
+ // Below are the available models. Will need to choose 3 to be available ONLY
728
+ const data = [
729
+ { name: "bert-base-cased", kind: tp.ModelKind.Bidirectional },
730
+ { name: "bert-base-uncased", kind: tp.ModelKind.Bidirectional },
731
+ { name: "distilbert-base-uncased", kind: tp.ModelKind.Bidirectional },
732
+ { name: "distilroberta-base", kind: tp.ModelKind.Bidirectional },
733
+ // { name: "roberta-base", kind: tp.ModelKind.Bidirectional },
734
+ { name: "gpt2", kind: tp.ModelKind.Autoregressive },
735
+ // { name: "gpt2-medium", kind: tp.ModelKind.Autoregressive },
736
+ // { name: "distilgpt2", kind: tp.ModelKind.Autoregressive },
737
+ ]
738
+
739
+ const names = R.map(R.prop('name'))(data)
740
+ const kinds = R.map(R.prop('kind'))(data)
741
+ const kindmap = R.zipObj(names, kinds)
742
+
743
+ this.sels.modelSelector.selectAll('.model-option')
744
+ .data(data)
745
+ .join('option')
746
+ .classed('model-option', true)
747
+ .property('value', d => d.name)
748
+ .attr("modelkind", d => d.kind)
749
+ .text(d => d.name)
750
+
751
+ this.sels.modelSelector.property('value', this.uiConf.model());
752
+
753
+ this.sels.modelSelector.on('change', function () {
754
+ const me = d3.select(this)
755
+ const mname = me.property('value')
756
+ self.uiConf.model(mname);
757
+ self.uiConf.modelKind(kindmap[mname]);
758
+ if (kindmap[mname] == tp.ModelKind.Autoregressive) {
759
+ console.log("RESETTING MASK INDS");
760
+ self.uiConf.maskInds([])
761
+ }
762
+ self.mainInit();
763
+ })
764
+ }
765
+
766
  renderAttHead() {
767
  const heads = _.range(0, this.uiConf._nHeads)
768
  const focusAtt = this.attCapsule.att
 
822
  this.render();
823
  }
824
  }
825
+
826
+
server/main.py CHANGED
@@ -7,7 +7,6 @@ from flask import render_template, redirect, send_from_directory
7
  import utils.path_fixes as pf
8
  from utils.f import ifnone
9
 
10
- from data_processing import from_model
11
  from model_api import get_details
12
 
13
  app = connexion.FlaskApp(__name__, static_folder="client/dist", specification_dir=".")
@@ -101,7 +100,7 @@ def get_attentions_and_preds(**request):
101
  sentence = request["sentence"]
102
  layer = int(request["layer"])
103
 
104
- deets = details.att_from_sentence(sentence)
105
 
106
  payload_out = deets.to_json(layer)
107
 
@@ -152,14 +151,14 @@ def update_masked_attention(**request):
152
  mask = payload["mask"]
153
  layer = int(payload["layer"])
154
 
155
- MASK = details.aligner.mask_token
156
  mask_tokens = lambda toks, maskinds: [
157
  t if i not in maskinds else ifnone(MASK, t) for (i, t) in enumerate(toks)
158
  ]
159
 
160
  token_inputs = mask_tokens(tokens, mask)
161
 
162
- deets = details.att_from_tokens(token_inputs, sentence)
163
  payload_out = deets.to_json(layer)
164
 
165
  return {
 
7
  import utils.path_fixes as pf
8
  from utils.f import ifnone
9
 
 
10
  from model_api import get_details
11
 
12
  app = connexion.FlaskApp(__name__, static_folder="client/dist", specification_dir=".")
 
100
  sentence = request["sentence"]
101
  layer = int(request["layer"])
102
 
103
+ deets = details.from_sentence(sentence)
104
 
105
  payload_out = deets.to_json(layer)
106
 
 
151
  mask = payload["mask"]
152
  layer = int(payload["layer"])
153
 
154
+ MASK = details.tok.mask_token
155
  mask_tokens = lambda toks, maskinds: [
156
  t if i not in maskinds else ifnone(MASK, t) for (i, t) in enumerate(toks)
157
  ]
158
 
159
  token_inputs = mask_tokens(tokens, mask)
160
 
161
+ deets = details.from_tokens(token_inputs, sentence)
162
  payload_out = deets.to_json(layer)
163
 
164
  return {
server/swagger.yaml CHANGED
@@ -32,7 +32,7 @@ paths:
32
  /attend+meta:
33
  get:
34
  tags: [All]
35
- operationId: main.get_attention_and_meta
36
  summary: Get the attention information, BERT Embeddings, and spacy meta info for an input sentence
37
  parameters:
38
  - name: model
@@ -66,81 +66,6 @@ paths:
66
  200:
67
  description: Update BERT's masked behavior for passed tokens
68
 
69
- /k-nearest-embeddings:
70
- get:
71
- tags: [All]
72
- operationId: main.nearest_embedding_search
73
- summary: Search for the nearest embeddings to a token sent from the frontend by layer
74
- parameters:
75
- - name: model
76
- description: Which model to get information from
77
- in: query
78
- type: string
79
- - name: corpus
80
- description: Which corpus to search
81
- in: query
82
- type: string
83
- - name: embedding
84
- description: Query vector on which to search the dataset
85
- in: query
86
- type: array
87
- items:
88
- type: number
89
- - name: layer
90
- description: Which layer to search the nearest for
91
- in: query
92
- type: number
93
- - name: heads
94
- description: List of heads to search for
95
- in: query
96
- type: array
97
- items:
98
- type: number
99
- - name: k
100
- description: How many nearest neighbors to grab
101
- in: query
102
- type: number
103
- responses:
104
- 200:
105
- description: Return related embeddings and associated metadata
106
-
107
- /k-nearest-contexts:
108
- get:
109
- tags: [All]
110
- operationId: main.nearest_context_search
111
- summary: Search for the nearest embeddings BY SELECTED HEADS to a token sent from the frontend by layer
112
- parameters:
113
- - name: model
114
- description: Which model to get information from
115
- in: query
116
- type: string
117
- - name: corpus
118
- description: Which corpus to search
119
- in: query
120
- type: string
121
- - name: context
122
- description: Query vector on which to search the dataset
123
- in: query
124
- type: array
125
- items:
126
- type: number
127
- - name: layer
128
- description: Which layer to search the nearest for
129
- in: query
130
- type: number
131
- - name: heads
132
- description: List of heads to search for
133
- in: query
134
- type: array
135
- items:
136
- type: number
137
- - name: k
138
- description: How many nearest neighbors to grab
139
- in: query
140
- type: number
141
- responses:
142
- 200:
143
- description: Return related embeddings by that head and the associated metadata
144
 
145
  definitions:
146
  maskPayload:
 
32
  /attend+meta:
33
  get:
34
  tags: [All]
35
+ operationId: main.get_attentions_and_preds
36
  summary: Get the attention information, BERT Embeddings, and spacy meta info for an input sentence
37
  parameters:
38
  - name: model
 
66
  200:
67
  description: Update BERT's masked behavior for passed tokens
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  definitions:
71
  maskPayload:
server/transformer_formatter.py CHANGED
@@ -65,9 +65,19 @@ class TransformerOutputFormatter:
65
  self.topk_probs = topk_probs
66
  self.model_config = model_config
67
 
68
- self.n_layer = self.model_config.n_layer
69
- self.n_head = self.model_config.n_head
70
- self.hidden_dim = self.model_config.n_embd
 
 
 
 
 
 
 
 
 
 
71
 
72
  self.__len = len(tokens)# Get the number of tokens in the input
73
  assert self.__len == self.attentions[0].shape[-1], "Attentions don't represent the passed tokens!"
 
65
  self.topk_probs = topk_probs
66
  self.model_config = model_config
67
 
68
+ try:
69
+ # GPT vals
70
+ self.n_layer = self.model_config.n_layer
71
+ self.n_head = self.model_config.n_head
72
+ self.hidden_dim = self.model_config.n_embd
73
+ except AttributeError:
74
+ try:
75
+ # BERT vals
76
+ self.n_layer = self.model_config.num_hidden_layers
77
+ self.n_head = self.model_config.num_attention_heads
78
+ self.hidden_dim = self.model_config.hidden_size
79
+ except AttributeError: raise
80
+
81
 
82
  self.__len = len(tokens)# Get the number of tokens in the input
83
  assert self.__len == self.attentions[0].shape[-1], "Attentions don't represent the passed tokens!"
server/utils/path_fixes.py CHANGED
@@ -5,7 +5,7 @@ FAISS_LAYER_PATTERN = 'layer_*.faiss'
5
  LAYER_TEMPLATE = 'layer_{:02d}.faiss'
6
 
7
  ROOT_DIR = Path(os.path.abspath(__file__)).parent.parent.parent
8
- CORPORA = ROOT / "corpora"
9
  DATA_DIR = ROOT_DIR / 'server' / 'data'
10
  DATASET_DIR = Path.home() / 'Datasets'
11
  ROOT_DIR = Path(os.path.abspath(__file__)).parent.parent.parent
 
5
  LAYER_TEMPLATE = 'layer_{:02d}.faiss'
6
 
7
  ROOT_DIR = Path(os.path.abspath(__file__)).parent.parent.parent
8
+ CORPORA = ROOT_DIR / "corpora"
9
  DATA_DIR = ROOT_DIR / 'server' / 'data'
10
  DATASET_DIR = Path.home() / 'Datasets'
11
  ROOT_DIR = Path(os.path.abspath(__file__)).parent.parent.parent