davanstrien HF staff commited on
Commit
9348d85
1 Parent(s): db4b478
Files changed (1) hide show
  1. app.py +182 -0
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from functools import lru_cache
2
+ from fastai.text.all import *
3
+ from fastcore.all import *
4
+ import matplotlib.cm as cm
5
+ import html
6
+ import gradio as gr
7
+
8
+ learn_inf = load_learner("20210928-model.pkl")
9
+
10
+
11
+ def _value2rgba(x, cmap=cm.RdYlGn, alpha_mult=1.0):
12
+ "Convert a value `x` from 0 to 1 (inclusive) to an RGBA tuple according to `cmap` times transparency `alpha_mult`."
13
+ c = cmap(x)
14
+ rgb = (np.array(c[:-1]) * 255).astype(int)
15
+ a = c[-1] * alpha_mult
16
+ return tuple(rgb.tolist() + [a])
17
+
18
+
19
+
20
+ def _eval_dropouts(mod):
21
+ module_name = mod.__class__.__name__
22
+ if "Dropout" in module_name or "BatchNorm" in module_name:
23
+ mod.training = False
24
+ for module in mod.children():
25
+ _eval_dropouts(module)
26
+
27
+
28
+
29
+ def _piece_attn_html(pieces, attns, sep=" ", **kwargs):
30
+ html_code, spans = ['<span style="font-family: monospace;">'], []
31
+ for p, a in zip(pieces, attns):
32
+ p = html.escape(p)
33
+ c = str(_value2rgba(a, alpha_mult=0.5, **kwargs))
34
+ spans.append(
35
+ f'<span title="{a:.3f}" style="background-color: rgba{c};">{p}</span>'
36
+ )
37
+ html_code.append(sep.join(spans))
38
+ html_code.append("</span>")
39
+ return "".join(html_code)
40
+
41
+
42
+ def _show_piece_attn(*args, **kwargs):
43
+ from IPython.display import display, HTML
44
+
45
+ display(HTML(_piece_attn_html(*args, **kwargs)))
46
+
47
+
48
+
49
+ @lru_cache(maxsize=1024*2)
50
+ def _intrinsic_attention(learn, text, class_id=None):
51
+ "Calculate the intrinsic attention of the input w.r.t to an output `class_id`, or the classification given by the model if `None`."
52
+ learn.model.train()
53
+ _eval_dropouts(learn.model)
54
+ learn.model.zero_grad()
55
+ learn.model.reset()
56
+ dl = learn.dls.test_dl([text])
57
+ batch = next(iter(dl))[0]
58
+ emb = learn.model[0].module.encoder(batch).detach().requires_grad_(True)
59
+ emb.retain_grad()
60
+ lstm = learn.model[0].module(emb, True)
61
+ learn.model.eval()
62
+ cl = learn.model[1]((lstm, torch.zeros_like(batch).bool(),))[
63
+ 0
64
+ ].softmax(dim=-1)
65
+ if class_id is None:
66
+ class_id = cl.argmax()
67
+ cl[0][class_id].backward()
68
+ attn = emb.grad.squeeze().abs().sum(dim=-1)
69
+ attn /= attn.max()
70
+ tok, _ = learn.dls.decode_batch((*tuplify(batch), *tuplify(cl)))[0]
71
+ return tok, attn
72
+
73
+
74
+ @patch
75
+ def intrinsic_attention(x: TextLearner, text: str, class_id: int = None, **kwargs):
76
+ "Shows the `intrinsic attention for `text`, optional `class_id`"
77
+ if isinstance(x, LMLearner):
78
+ raise Exception("Language models are not supported")
79
+ text, attn = _intrinsic_attention(x, text, class_id)
80
+ return _piece_attn_html(text.split(), to_np(attn), **kwargs)
81
+
82
+
83
+
84
+
85
+ labels = learn_inf.dls.vocab[1]
86
+
87
+
88
+ @lru_cache(maxsize=1024*2)
89
+ def predict_label(title):
90
+ *_, probs = learn_inf.predict(title)
91
+ return probs
92
+
93
+
94
+ def predict(title):
95
+ # *_, probs = learn_inf.predict(title)
96
+
97
+ probs = predict_label(title)
98
+ return learn_inf.intrinsic_attention(title), {
99
+ labels[i]: float(probs[i]) for i in range(len(labels))
100
+ }
101
+
102
+
103
+ sample_text = [
104
+ [
105
+ "Poems on various subjects. Whereto is prefixed a short essay on the structure of English verse"
106
+ ],
107
+ [
108
+ "Journal of a Residence in China and the neighbouring countries from 1830 to 1833. With an introductory essay by the Hon. and Rev. Baptist Wriothesley Noel. [With a map.]"
109
+ ],
110
+ ["The Adventures of Oliver Twist. [With plates.]"],
111
+ ["['The Adventures of Sherlock Holmes', 'Single Works']"],
112
+ [
113
+ "['Coal, Iron, and Oil; or, the Practical American miner. A plain and popular work on our mines and mineral resources ... With numerous maps and engravings, etc']"
114
+ ],
115
+ [
116
+ "Summer Travelling in Iceland; being the narrative of two journeys across the island ... With a chapter on Askja by E. Delmar Morgan ... Containing also a literal translation of three sagas. Maps, etc'"
117
+ ],
118
+ [
119
+ "Histoire de France au moyen aÃÇge, depuis Philippe-Auguste jusqu'aÃÄ la fin du reÃÄgne de Louis XI. 1223-1483. Troisieme eÃÅdition"
120
+ ],
121
+ [
122
+ "Two Centuries of Soho: its institutions, firms, and amusements. By the Clergy of St. Anne's, Soho, J. H. Cardwell ... H. B. Freeman ... G. C. Wilton ... assisted by other contributors, etc"
123
+ ],
124
+ ["""A Christmas Carol"""],
125
+ ]
126
+
127
+ description = """
128
+ British Library Books genre detection model
129
+ """
130
+
131
+ article = """
132
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5245175.svg)](https://doi.org/10.5281/zenodo.5245175)
133
+
134
+
135
+ # British Library Books genre detection demo
136
+
137
+ This demo alows you to play with a 'genre' detection model which has been trained to predict, from the title of a book, whether it is 'fiction' or 'non-fiction'.
138
+ The demo also shows you which parts of the input the model is using most to make its prediction.
139
+
140
+ ## Model description
141
+
142
+ This model is intended to predict, from the title of a book, whether it is 'fiction' or 'non-fiction'. This model was trained on data created from the [Digitised printed books (18th-19th Century)](https://www.bl.uk/collection-guides/digitised-printed-books) book collection.
143
+ This dataset is dominated by English language books though it includes books in several other languages in much smaller numbers. This model was originally developed for use as part of the Living with Machines project to be able to 'segment' this large dataset of books into different categories based on a 'crude' classification of genre i.e. whether the title was `fiction` or `non-fiction`.
144
+
145
+
146
+ ## Training data
147
+
148
+ [[More information needed]]
149
+
150
+ ## Model performance
151
+
152
+ The models performance on a held-out test set is as follows:
153
+
154
+ ```
155
+ precision recall f1-score support
156
+
157
+ Fiction 0.91 0.88 0.90 296
158
+ Non-fiction 0.94 0.95 0.95 554
159
+
160
+ accuracy 0.93 850
161
+ macro avg 0.93 0.92 0.92 850
162
+ weighted avg 0.93 0.93 0.93 850
163
+ ```
164
+
165
+ > Credit: This work was partly supported by [Living with Machines](https://livingwithmachines.ac.uk/). This project, funded by the UK Research and Innovation (UKRI) Strategic Priority Fund, is a multidisciplinary collaboration delivered by the Arts and Humanities Research Council (AHRC), with The Alan Turing Institute, the British Library and the Universities of Cambridge, East Anglia, Exeter, and Queen Mary University of London.
166
+
167
+ """
168
+
169
+ gr_interface = gr.Interface(
170
+ fn=predict,
171
+ inputs=gr.inputs.Textbox(),
172
+ outputs=[
173
+ gr.outputs.HTML("Intrinsic attention"),
174
+ gr.outputs.Label(num_top_classes=len(labels), label="Confidence"),
175
+ ],
176
+ title="British Library 19th Century Books Genre Classifier",
177
+ description=description,
178
+ article=article,
179
+ examples=sample_text,
180
+ allow_screenshot=True,
181
+ )
182
+ gr_interface.launch(inline=False, share=False)