 **Chapter 5: [Instrument](CH5_00-Instrument.ipynb)**

# Apertures, Deflectors, and Magnetic Lenses

[Download](https://raw.githubusercontent.com/gduscher/MSE672-Introduction-to-TEM//main/Instrument/CH5_03-Lenses.ipynb)

[](
    https://colab.research.google.com/github/gduscher/MSE672-Introduction-to-TEM/blob/main/Instrument/CH5_03-Lenses.ipynb)

part of

 **[MSE672:  Introduction to Transmission Electron Microscopy](../_MSE672_Intro_TEM.ipynb)**

by Gerd Duscher, Spring 2021

Microscopy Facilities

Joint Institute of Advanced Materials

Materials Science & Engineering

The University of Tennessee, Knoxville

Background and methods to analysis and quantification of data acquired with transmission electron microscopes.

## Magnetic Lenses
Electro--magnetic lenses are different from optical lenses: You can change the focal length.

For a deeper understanding and a better operation of the TEM, we have to discuss the details.

Electro--static lenses are not common anymore, because the long term stability is hard to achieve.
Magnetic lenses are much easier to keep stable.

The cut through a magnetic lens. Please note that all lenses must be radial symmetric.\\

The coil produces a strong magnetic field (up to 1 Tesla or higher), which is concentrated at the pole piece.
This pole piece consists of soft iron to achieve a maximal inhomogeneous magnetic field (2.1 Tesla).
At room temperature  you cannot achieve a higher field than the maximum magnetic field of iron.

Because pure iron is a very soft material, this pole piece has to be handled with extreme care.
Any scratch, inhomogeneity or dirt (results from magnetic samples which FLY to the pole piece) destroy
the radial symmetry and induce optical errors.

The gap is needed to place apertures and the sample close to the focal planes.
This gap doesn't cause a problem at weak lenses, but at strong lenses (objective lens) this gap induces optical errors.
The focal length of a strong lens is also short, and the sample has o be paced inside the objective lens. High resolution lenses always exhibit a small gap to minimize optical errors which results in limited sample tilt.

TEM manufactures invest a lot of money and time to develop new designs of the pole pieces and lens designs.

The coils are made of wires with a low but still some resistance. this resistance heats the lenses and water pipes are integrated for cooling.

TEM manufactures invest a lot of money and time to develop new designs of the pole pieces and lens designs.

```python

```

### Electrons in a Magnetic Field

The main equation which describes the motion of an electron in an electro-magnetic field is:
$$
\begin{equation}\Large \label{magnetic force}
\vec{F} = q (\vec{E} + \vec{v} \times \vec{B}) = e(\vec{v} \times \vec{B})
\end{equation}
$$
The force $\vec{F}$ on an electron with charge $q=e$ depends on the electric field $\vec{E}$ and the vector cross product ($\times$) of velocity $\vec{v}$ and magnetic field $\vec{B}$ (measured in Tesla).

For magnetic fields we can ignore the effect of the electric field. The effect of the magnetic field is described by the left hand rule as pictured below.

The forefinger points in the direction of the magnetic field $\vec{B}$, the second finger in the direction the electron flies initially and the thumb will give you the direction of the thrust.

This left hand rule shows immediately that for a $\theta$ of 0$\rm ^o$ and 180$\rm ^o$, we do not know what to do and really there is no thrust. The thrust is maximum for a $\theta$ of 90$\rm ^o$.

For small deviation angles we can make the approximation for the magnitude of the force:
$$
F =  e v B \sin \theta \approx e v B = \frac{mv^2}{r}
$$
The smaller the distance from the optic axis $r$ the higher is the force. The higher the velocity of the electrons the stronger the lens has to be.

For 100 keV electrons and a lens diameter of 1 mm, the magnetic field $B$ has to be in the order of 1 Tesla.

Any direction of the beam can be split in a motion parallel $\vec{v}_{\|}$ and perpendicular $\vec{v}_{\bot}$ to the magnetic field direction.
$$
\Large
\vec{v}_{\|} = v \sin \theta
$$
$$
\Large
\vec{v}_{\bot} = v \cos \theta
$$

The parallel motion is undisturbed while the perpendicular motion will result in a circle.
The radius of this  circle is $$\Large r=\frac{mv}{eB}$$.

Both motions together result in a spiral motion with the frequency $$ \Large \omega_c = \frac{2\pi}{T_c}=\frac{eB}{m}\not\approx v$$.

Mathematical, we can write for the near axis motion the paraxial ray equations.
$$
\frac{d^2r}{dz2}+\frac {\eta B^2 r}{2V^{\frac{1}{2}}} = 0
$$
$$
\frac{d\theta}{dz}=\frac {\eta B}{2V^{\frac{1}{2}}} = 0
$$

with
$$
\eta = \sqrt{\frac{e}{2m_0c^2}}
$$

This results in a smaller $\Delta r$ for higher energetic electrons (higher acceleration Voltage $V$), and therefore in the need to increase the magnetic field.

Because real lenses are not indefinite thin, but have a certain length, there will always be spiral motion and therefore an image rotation associated with a magnetic lens. The TEM manufactures have a variety of lens settings for different magnifications, which can minimize or cancel image rotation.

## Apertures

As you know you can put apertures in and out, also you can move them, so that they are aligned with respect to the optic axis.
These apertures are little plates with small hopefully round holes in it; they are called diaphragm. The holes are about 10 $\mu$m to 100 $\mu$m in diameter.

The diaphragm are made of molybdenum, copper or gold.
The gold apertures have a self cleaning effect, if you hit them with an intense electron beam and are therefore preferable in a TEM. You cannot use them in a dedicated STEM, where the beam current is much lower.

## Electron Beam Deflection

Aligning the microscope means to bring all the optical elements (electron source, apertures, lenses, ...) onto the same optical axis. To do this a rough alignment is done mechanically. The lenses are pushed and tilted close to a common optic axis. The day to day alignment of the TEM is done electronically with deflectors. These alignment deflectors (with long time stability) are normally magnetic.

In a scanning TEM (or SEM), where the beam has to be moved around, electro--static deflectors are used.
Also other deflectors, which have to be fast (beam blanker), use electro--static forces.
While a magnetic deflector need a few microseconds, a electro--static one can do in a split of a microsecond.

For the magnetic deflectors, the deflection angle $\epsilon$ is give by:
\begin{equation}\Large
\epsilon = \frac{eLB}{mv}
\end{equation}

For a 5$^{\rm o}$ deflection of 100keV electrons we need 0.01 Tesla. That means 100 turn coil with 0.2 A.

We will go a little bit more in detail with the electro--static deflection.\\

We have the following equations:
$$ \Large v=\frac{dz}{dt};$$$$ \Large p_z=mv;$$$$ \Large T=\frac{L}{v}$$

Deflection of an electron in an electro--static field $\vec{E}$

For the momentum in x direction we get:
\begin{equation}\Large
p_x = \int_0^T F dt= e \int_0^T E dt = \frac{e}{v} \int_0^L E dz = \frac{eEL}{v}
\end{equation}

For 100 keV electrons in a deflector (capacitor) need an electric field of 20000 V/cm to be deflected by 0.1 rad (5$\rm o$)  For that you have to apply a Voltage of 4000V to a deflector with a length $L$ of 1cm and a width $d$ of 2mm.

```python

```


# Feed-forward and Recurrent Language Models

### Preliminaries

```python
%pylab inline
%load_ext autoreload
%autoreload 2
```

    Populating the interactive namespace from numpy and matplotlib

```python
# !pip install jsonlines tqdm
```

    Requirement already satisfied: jsonlines in /home/aims/anaconda3/envs/aims/lib/python3.7/site-packages (1.2.0)
    Requirement already satisfied: tqdm in /home/aims/anaconda3/envs/aims/lib/python3.7/site-packages (4.37.0)
    Requirement already satisfied: six in /home/aims/anaconda3/envs/aims/lib/python3.7/site-packages (from jsonlines) (1.12.0)

```python
# download pretrained models and data
### DOWNLOADING THE FILES
import os

### persona chat dataset
if not os.path.exists('personachat_all_sentences_train.jsonl'):
    !wget "https://nyu.box.com/shared/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl" -O 'personachat_all_sentences_train.jsonl'
if not os.path.exists('personachat_all_sentences_valid.jsonl'):
    !wget "https://nyu.box.com/shared/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl" -O 'personachat_all_sentences_valid.jsonl'
if not os.path.exists('personachat_all_sentences_test.jsonl'):
    !wget "https://nyu.box.com/shared/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl" -O 'personachat_all_sentences_test.jsonl'

## wikitext-2 dataset
if not os.path.exists('wikitext2-sentencized.json'):
    !wget "https://nyu.box.com/shared/static/9kb7l7ci30hb6uahhbssjlq0kctr5ii4.json" -O 'wikitext2-sentencized.json'

### pretrained ffnn model
if not os.path.exists('personachat_ffnn_lm.pt'):
    !wget "https://nyu.box.com/shared/static/looczvct7ssh30x0gwqmvencple8xok9.pt" -O 'personachat_ffnn_lm.pt'

### pretrained rnn model
if not os.path.exists('personachat_rnn_lm.pt'):
    !wget "https://nyu.box.com/shared/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt" -O 'personachat_rnn_lm.pt'
```

    --2020-03-22 23:46:58--  https://nyu.box.com/shared/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl
    Resolving nyu.box.com (nyu.box.com)... 185.235.236.197
    Connecting to nyu.box.com (nyu.box.com)|185.235.236.197|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /public/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl [following]
    --2020-03-22 23:46:59--  https://nyu.box.com/public/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl
    Reusing existing connection to nyu.box.com:443.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://nyu.app.box.com/public/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl [following]
    --2020-03-22 23:47:00--  https://nyu.app.box.com/public/static/q4nvswb0szelivhgyx87vd1056ttqfyi.jsonl
    Resolving nyu.app.box.com (nyu.app.box.com)... 185.235.236.199
    Connecting to nyu.app.box.com (nyu.app.box.com)|185.235.236.199|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://public.boxcloud.com/d/1/b1!YIeGz5LZnD3LgCNn1HqoEC8Z3wwOag3NruX5bLV_WvuiRA-wJsKRUqmcjXJBITAwm5CDA46N7KZq75j_SZNEUSijo_GLd4cMYeefLVgo7StgVgwbZtwAXpPFRRYBeoZewz-4ZZchsuLpehwLIOSKa3sdVxFohzPCAXIwUF2mlbT-SuuvsRFQxlxpPAnYvTuHJfa8gWcn6FIiWIR97xBOCAZ8uIk12LtbUJdB-K3PsGhdrnRsHFi-E3D0oSjg0TQeBN895B83FWijqdP6nvLPrIi2s8ReRxUmRCXPpFI5OzulAsK6MUAFfGGhyFjwiyvawW120MO33gYREppWP13fPfu_Xlh0bsUmbxfhb6NmhG7BJf62fU22HQpprbU3EUfPlbMCE-EhAFsNfzEOABdYqEQRcqnaSJctfxNgQQTrVGwYLHL8hGYdFJxgCQYKNH9x6U7QXyKeNyPcmeqb0U05CKnEn4dHD_RohHvJdft_kF-_2y_mErYmolMrFP30i84A42Z82PttWsZOXazC-Pzp6YibzC15oJt6INbMUcd4_B77GFJ4m_0gSs3aAOa5UaaJluqNV0k2BOAh8UkV6IhrT8abCemnIAzbbMPVGeHNv5nok4kZ8wnqVhxsgbpSVH3zbZ8hC6QsI0oTsgXxcgNKgJ9mFiNtU_oIeJhpkxwU4hX-iEfS6RUrKcGMwDeeo9vz6_5obubppTjqU9A_JWlNEzSZSWNfORmGvgXSBARjEUstgwPy8UuRozwUkZA1CYR531xA5XYiIkbZlFKcu7rQgx2SQRxdL0FKtbkVih7I_QeBZ2qArE22J42j8TPX0s25IiE2FyWbRJb0UQy7WFwTWecve3kKK_m8aEj_hHPooicUMdmQcpttKI0kO4J5vnRC3732KdGkfvKd5K4zlPnHvNoRr5AIhF6AXV_JA0BAehtKMu3Y8v6fVqZXjdsACxHxrjRrBVTSkOirrYYydQYF2DgS4iRex4bBglXWDYFs-Et8HVz_kJk6Ui0S7C-R3DvBeEr0JGL_6mmDbiJ7BZMhMn1OMdAb3uzYZ8UOEi3JFXB4YRj29Y9psPW6ynmsLf7Fx-tkiww-V_SSmskec4ybw1VHJGnGydcFVPdhoE_lHpUN9rbZThk1WL9KHg8j51f8HkcvEFW0aXvdqkDGDHlXiEwAq2UrywjOrgEPXQJN9DO_nPZoQdfYJkuBN22Q5SWbOVtEUwcJ2_DCZCZK1YEm5VBE6hPuorackebXYTI9lmNUZqR_msFzjQ4Uh7C3U6qSejbhyKQsTnZ0VpC_0XAikX9OdyXIecej3aVqrIeG9KBtrE_8C1QKnu2QekCaIfrQyANpLM3WvC3-vvO0gojf-dsWS0rDKyVmcnlmACdfnmJagRsd9V8H7vE755JONSKQIR4NU5kX2BGi8APSzTA2i9rN73tp1Q../download [following]
    --2020-03-22 23:47:01--  https://public.boxcloud.com/d/1/b1!YIeGz5LZnD3LgCNn1HqoEC8Z3wwOag3NruX5bLV_WvuiRA-wJsKRUqmcjXJBITAwm5CDA46N7KZq75j_SZNEUSijo_GLd4cMYeefLVgo7StgVgwbZtwAXpPFRRYBeoZewz-4ZZchsuLpehwLIOSKa3sdVxFohzPCAXIwUF2mlbT-SuuvsRFQxlxpPAnYvTuHJfa8gWcn6FIiWIR97xBOCAZ8uIk12LtbUJdB-K3PsGhdrnRsHFi-E3D0oSjg0TQeBN895B83FWijqdP6nvLPrIi2s8ReRxUmRCXPpFI5OzulAsK6MUAFfGGhyFjwiyvawW120MO33gYREppWP13fPfu_Xlh0bsUmbxfhb6NmhG7BJf62fU22HQpprbU3EUfPlbMCE-EhAFsNfzEOABdYqEQRcqnaSJctfxNgQQTrVGwYLHL8hGYdFJxgCQYKNH9x6U7QXyKeNyPcmeqb0U05CKnEn4dHD_RohHvJdft_kF-_2y_mErYmolMrFP30i84A42Z82PttWsZOXazC-Pzp6YibzC15oJt6INbMUcd4_B77GFJ4m_0gSs3aAOa5UaaJluqNV0k2BOAh8UkV6IhrT8abCemnIAzbbMPVGeHNv5nok4kZ8wnqVhxsgbpSVH3zbZ8hC6QsI0oTsgXxcgNKgJ9mFiNtU_oIeJhpkxwU4hX-iEfS6RUrKcGMwDeeo9vz6_5obubppTjqU9A_JWlNEzSZSWNfORmGvgXSBARjEUstgwPy8UuRozwUkZA1CYR531xA5XYiIkbZlFKcu7rQgx2SQRxdL0FKtbkVih7I_QeBZ2qArE22J42j8TPX0s25IiE2FyWbRJb0UQy7WFwTWecve3kKK_m8aEj_hHPooicUMdmQcpttKI0kO4J5vnRC3732KdGkfvKd5K4zlPnHvNoRr5AIhF6AXV_JA0BAehtKMu3Y8v6fVqZXjdsACxHxrjRrBVTSkOirrYYydQYF2DgS4iRex4bBglXWDYFs-Et8HVz_kJk6Ui0S7C-R3DvBeEr0JGL_6mmDbiJ7BZMhMn1OMdAb3uzYZ8UOEi3JFXB4YRj29Y9psPW6ynmsLf7Fx-tkiww-V_SSmskec4ybw1VHJGnGydcFVPdhoE_lHpUN9rbZThk1WL9KHg8j51f8HkcvEFW0aXvdqkDGDHlXiEwAq2UrywjOrgEPXQJN9DO_nPZoQdfYJkuBN22Q5SWbOVtEUwcJ2_DCZCZK1YEm5VBE6hPuorackebXYTI9lmNUZqR_msFzjQ4Uh7C3U6qSejbhyKQsTnZ0VpC_0XAikX9OdyXIecej3aVqrIeG9KBtrE_8C1QKnu2QekCaIfrQyANpLM3WvC3-vvO0gojf-dsWS0rDKyVmcnlmACdfnmJagRsd9V8H7vE755JONSKQIR4NU5kX2BGi8APSzTA2i9rN73tp1Q../download
    Resolving public.boxcloud.com (public.boxcloud.com)... 185.235.236.200
    Connecting to public.boxcloud.com (public.boxcloud.com)|185.235.236.200|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 13403897 (13M) [application/octet-stream]
    Saving to: ‘personachat_all_sentences_train.jsonl’

    personachat_all_sen 100%[===================>]  12.78M  7.56MB/s    in 1.7s

    2020-03-22 23:47:04 (7.56 MB/s) - ‘personachat_all_sentences_train.jsonl’ saved [13403897/13403897]

    --2020-03-22 23:47:04--  https://nyu.box.com/shared/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl
    Resolving nyu.box.com (nyu.box.com)... 185.235.236.197
    Connecting to nyu.box.com (nyu.box.com)|185.235.236.197|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /public/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl [following]
    --2020-03-22 23:47:05--  https://nyu.box.com/public/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl
    Reusing existing connection to nyu.box.com:443.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://nyu.app.box.com/public/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl [following]
    --2020-03-22 23:47:05--  https://nyu.app.box.com/public/static/8krcizo8sms1m0ppy7uiwfcx4a3l5nsq.jsonl
    Resolving nyu.app.box.com (nyu.app.box.com)... 185.235.236.199
    Connecting to nyu.app.box.com (nyu.app.box.com)|185.235.236.199|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://public.boxcloud.com/d/1/b1!P7f2tWjqhlo7S-r0GmhNqvqLPhS8TMjKniY0i8CA8cO2WwRHWu_MD9GBqc0N9oqIiBOZoA6hd2f-_epLsWslDXUNfwlSRbhs97rRZopfuZPjfS9QMIQhq5j8BkIiAaNQV15MHpSsVubGqEokWZrX-7bOEqCb2TXf11UPompaji9ZgpDHYF3WC2FPu4MgG5YeBJAN9Or4BI2F3aAzLqnPBy80yngPHe3qah0-GelRpKI7v7F-6IG8QbQPkRAhch6T8CUt3aegjreytJK8vO3gOn0hqec-9sVaduo0q98JJX6VB_jvO6eX0Jy_nfi2jvqkPaol2xqUHtaKboHypvS5j2ix26-HG_QrCnVifeJFlnHe4_lPlKxo96hyCr6kIjiTdGN0N4aZABKnFEIarI333HKqqULDKiucdXYpPown83h2n4oMDDUZrDSsyJmAKGOAkJaJQUgsIMX8nY7Tk5BndEgCzaMSDJMW5McO5pLQOMWFOWQ5gGKLjyP-IBci8A0JgqyQX_noGfZ-s1SRt_ZIojohnaaYw1wYWcwObblSCB6QLukZlewhD__7ZhaG9Xy-ljjTjC3UHgpIOMo2FPb304noQpGl9ywYEF4uyuHbGEIfDbV6JQ__OUlsFQH7OXeTd06gRyJdTFTuRvnhjAoJEBYIP8RCfBCtoegoZaRJfqBMMDYJ7_s5jbtsRVLyZMlwaz4St4oDuYpHr1lWN9BToRqhhE7glHghs_S4s-4sl-XCjVNmiYXMQ82UfFwcPHroqsyqKr-bBDQ1VOipNeIku5d0GgnuV6fLjbk9R2Gjjzcjh13cN68m_d5iry1lsWZwIYd7Sggkz3TAn8DruogG7w8TSATWaTy4exFRN3ZFnyFKHfok_2Mx49RnZnBG1aJAY_EZBatkCj9WLoLAXWXUcFoTtRwCks8AOkPFSQKWE3YuTUPAlRckTDims_JQ0t9f_Lj-rO_-65C_HbNymtYKtdK6edYV7IdEadvM2sHksmN73LrcAbCaV884OKALNyEbBEyE0INpZgJsC0Yn9avAVytvnrGCxXev_E0tpspk0cL6TV1RQ5jqSCogv7mVsWeZ1a4Oo0nuTou3C49c845gInctU2s-RI19Cyqcry-MfbKtfohvquuBTLMRfWy1NpmjiMHAEht-zafOCuWj1ILpv8XQIdwjNAF64Xq2XMU1cfHCImn5oUodN61DHH8dMPQAiDOrKoewY__UtSX-BSaxceA439qxgpHHViLtgE3Gk2GD9S1ZLr5oiXb-oa7y7NcYM726-pnUJX5ZNinqUqOVUvN0Es3Tpe9iSOlc6lUdD8MQksvlu7G2vRAxh-KaabCbIy4_kxOlMaqfIsBg7NFoebTM_ZzVyaz0IVgbNHqN5sGMYQv6m4Df3-o2kwwd-i7MRFHlOVscJjBnvT6s2pGyONMbGGI9/download [following]
    --2020-03-22 23:47:06--  https://public.boxcloud.com/d/1/b1!P7f2tWjqhlo7S-r0GmhNqvqLPhS8TMjKniY0i8CA8cO2WwRHWu_MD9GBqc0N9oqIiBOZoA6hd2f-_epLsWslDXUNfwlSRbhs97rRZopfuZPjfS9QMIQhq5j8BkIiAaNQV15MHpSsVubGqEokWZrX-7bOEqCb2TXf11UPompaji9ZgpDHYF3WC2FPu4MgG5YeBJAN9Or4BI2F3aAzLqnPBy80yngPHe3qah0-GelRpKI7v7F-6IG8QbQPkRAhch6T8CUt3aegjreytJK8vO3gOn0hqec-9sVaduo0q98JJX6VB_jvO6eX0Jy_nfi2jvqkPaol2xqUHtaKboHypvS5j2ix26-HG_QrCnVifeJFlnHe4_lPlKxo96hyCr6kIjiTdGN0N4aZABKnFEIarI333HKqqULDKiucdXYpPown83h2n4oMDDUZrDSsyJmAKGOAkJaJQUgsIMX8nY7Tk5BndEgCzaMSDJMW5McO5pLQOMWFOWQ5gGKLjyP-IBci8A0JgqyQX_noGfZ-s1SRt_ZIojohnaaYw1wYWcwObblSCB6QLukZlewhD__7ZhaG9Xy-ljjTjC3UHgpIOMo2FPb304noQpGl9ywYEF4uyuHbGEIfDbV6JQ__OUlsFQH7OXeTd06gRyJdTFTuRvnhjAoJEBYIP8RCfBCtoegoZaRJfqBMMDYJ7_s5jbtsRVLyZMlwaz4St4oDuYpHr1lWN9BToRqhhE7glHghs_S4s-4sl-XCjVNmiYXMQ82UfFwcPHroqsyqKr-bBDQ1VOipNeIku5d0GgnuV6fLjbk9R2Gjjzcjh13cN68m_d5iry1lsWZwIYd7Sggkz3TAn8DruogG7w8TSATWaTy4exFRN3ZFnyFKHfok_2Mx49RnZnBG1aJAY_EZBatkCj9WLoLAXWXUcFoTtRwCks8AOkPFSQKWE3YuTUPAlRckTDims_JQ0t9f_Lj-rO_-65C_HbNymtYKtdK6edYV7IdEadvM2sHksmN73LrcAbCaV884OKALNyEbBEyE0INpZgJsC0Yn9avAVytvnrGCxXev_E0tpspk0cL6TV1RQ5jqSCogv7mVsWeZ1a4Oo0nuTou3C49c845gInctU2s-RI19Cyqcry-MfbKtfohvquuBTLMRfWy1NpmjiMHAEht-zafOCuWj1ILpv8XQIdwjNAF64Xq2XMU1cfHCImn5oUodN61DHH8dMPQAiDOrKoewY__UtSX-BSaxceA439qxgpHHViLtgE3Gk2GD9S1ZLr5oiXb-oa7y7NcYM726-pnUJX5ZNinqUqOVUvN0Es3Tpe9iSOlc6lUdD8MQksvlu7G2vRAxh-KaabCbIy4_kxOlMaqfIsBg7NFoebTM_ZzVyaz0IVgbNHqN5sGMYQv6m4Df3-o2kwwd-i7MRFHlOVscJjBnvT6s2pGyONMbGGI9/download
    Resolving public.boxcloud.com (public.boxcloud.com)... 185.235.236.200
    Connecting to public.boxcloud.com (public.boxcloud.com)|185.235.236.200|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1640194 (1.6M) [application/octet-stream]
    Saving to: ‘personachat_all_sentences_valid.jsonl’

    personachat_all_sen 100%[===================>]   1.56M  2.06MB/s    in 0.8s

    2020-03-22 23:47:13 (2.06 MB/s) - ‘personachat_all_sentences_valid.jsonl’ saved [1640194/1640194]

    --2020-03-22 23:47:13--  https://nyu.box.com/shared/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl
    Resolving nyu.box.com (nyu.box.com)... 185.235.236.197
    Connecting to nyu.box.com (nyu.box.com)|185.235.236.197|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /public/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl [following]
    --2020-03-22 23:47:14--  https://nyu.box.com/public/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl
    Reusing existing connection to nyu.box.com:443.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://nyu.app.box.com/public/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl [following]
    --2020-03-22 23:47:14--  https://nyu.app.box.com/public/static/im7we9k2gcf8kslqnfamsimicgosuw9y.jsonl
    Resolving nyu.app.box.com (nyu.app.box.com)... 185.235.236.199
    Connecting to nyu.app.box.com (nyu.app.box.com)|185.235.236.199|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://public.boxcloud.com/d/1/b1!G-57BfL6LiaVX-qmwd_xcPWOYxTJyG_g-9tAGRgwR1D3ghnIluLMiB189SCWw6x_oS45ajbKq2ON3SDnLdN_uen9tH7mGGKJiGUaTb6tYd-oCVwZR1rbo5UE60N12etNPt-RCCawzFBkxiZQqDM_7TR1mHiyESmKE9zC54OYtaD77UQX2AW5G9E8_kePMgCJHg7KQfcQKbll3rvYov_1CoYowRvbr_BlMTECx8JPcU107RB9wQrK3iiKq7gWVxKavkp8CyMfYw0D_9CmhrjMsEo4q4AmxHNsI6m_60r3UcnyIf_hDP3DSZ0iv0FhNunQnuaWPjFGwOaQVgOHQIjNSCejdIVjmuDw6Sf75t7aE4ddfI_T2EijAPPEWWMJ33EPr6jY0wsh6Gq7fZWYdpC-lPHOWMGwGWBv7Iz3k3hkLQ6wJlV_PNP1yDxixnE1IXf-TFuuEUpt9m2rcVn_mVLg_6utMYFY21_gWtZa2hJmaho74i0JdFQZsGAThtsvfiHHVqoFOdGuniBNrbYECMYj7umAzVUy8KTz8kJlb5oqfsKg88Rx3R2iw0GOWeVOGBZbnpkr1-q2lec-RjZTe6MUrIfNoSyuqayCW-x8Bg_GSElI31rBzfhVp_rV6nwyet2byUbo-vkFCY-62NS21dTXp1MszSxHpPb_XSSMtt1WougXAjOAY2QfKOpeDeQ_gk4UlUytzQ7D4h-1ir97fKeeFQcPbVVvPrPLera9tBpPKvtG04sG6LrWtAm9gGX3M7XdHu6ZZaSJqlOCQSsb9liVktcqB-69KS0IhJQ0NXj3U-EOZf-FiQ8ExyRYAYKKJzJkn3aCOJe2N8zf3-ZhAgaEIzfuw27XB0L0o-RwI4Y3oXviaeQDvt41welX7gogVAKQJFs_UsHdDJJptZjiXlQRfX5rxgMqC0GPZ8KAdjKT1RLwJKf1jx66Q7wjVYAVus7famqbq7MJbb_7U7XtqsSLzf7aw7n2cpmgoah0iRpcevkm8HdYCIvRcvE5BYuASBSm8hmNgsVxUDFoscaMqnOUQDITlRDBUIq2cKiiCvwfLRajzRJ1JQcNkVlUTiPwcGi_Zkd-W1eJI5jAE6xgNJq9JOkrYbN_tbntzS8bP3ekmo4_8WiOBEXOJD3zQI8ocXorxNIKLwvoXYYucGiaUAU3XdVR4jwIbzjUBAblRfgxS1sC010cUFoRKQkXDaC7lWBrhRzuX3BWq2Lba9QWjGi1_2UVb0vFEmt2SVvGFrbMXrE35it3HeGxfVsKj21-4Xokkryoc3jqzdMrNkVtRipa5Odq9nPRv6VjNm2fqYcrtY_Mdn0U5lzzjkmJ4_rT9VBvihLSizlBA4dP--kx3TIjqTyYjRFV2zArRNd8IqlFwy1OqHF1lPog4Q_P1FFNQ_uVKDlhCkd4m8fsSrXFlDr6I5AUTJU./download [following]
    --2020-03-22 23:47:15--  https://public.boxcloud.com/d/1/b1!G-57BfL6LiaVX-qmwd_xcPWOYxTJyG_g-9tAGRgwR1D3ghnIluLMiB189SCWw6x_oS45ajbKq2ON3SDnLdN_uen9tH7mGGKJiGUaTb6tYd-oCVwZR1rbo5UE60N12etNPt-RCCawzFBkxiZQqDM_7TR1mHiyESmKE9zC54OYtaD77UQX2AW5G9E8_kePMgCJHg7KQfcQKbll3rvYov_1CoYowRvbr_BlMTECx8JPcU107RB9wQrK3iiKq7gWVxKavkp8CyMfYw0D_9CmhrjMsEo4q4AmxHNsI6m_60r3UcnyIf_hDP3DSZ0iv0FhNunQnuaWPjFGwOaQVgOHQIjNSCejdIVjmuDw6Sf75t7aE4ddfI_T2EijAPPEWWMJ33EPr6jY0wsh6Gq7fZWYdpC-lPHOWMGwGWBv7Iz3k3hkLQ6wJlV_PNP1yDxixnE1IXf-TFuuEUpt9m2rcVn_mVLg_6utMYFY21_gWtZa2hJmaho74i0JdFQZsGAThtsvfiHHVqoFOdGuniBNrbYECMYj7umAzVUy8KTz8kJlb5oqfsKg88Rx3R2iw0GOWeVOGBZbnpkr1-q2lec-RjZTe6MUrIfNoSyuqayCW-x8Bg_GSElI31rBzfhVp_rV6nwyet2byUbo-vkFCY-62NS21dTXp1MszSxHpPb_XSSMtt1WougXAjOAY2QfKOpeDeQ_gk4UlUytzQ7D4h-1ir97fKeeFQcPbVVvPrPLera9tBpPKvtG04sG6LrWtAm9gGX3M7XdHu6ZZaSJqlOCQSsb9liVktcqB-69KS0IhJQ0NXj3U-EOZf-FiQ8ExyRYAYKKJzJkn3aCOJe2N8zf3-ZhAgaEIzfuw27XB0L0o-RwI4Y3oXviaeQDvt41welX7gogVAKQJFs_UsHdDJJptZjiXlQRfX5rxgMqC0GPZ8KAdjKT1RLwJKf1jx66Q7wjVYAVus7famqbq7MJbb_7U7XtqsSLzf7aw7n2cpmgoah0iRpcevkm8HdYCIvRcvE5BYuASBSm8hmNgsVxUDFoscaMqnOUQDITlRDBUIq2cKiiCvwfLRajzRJ1JQcNkVlUTiPwcGi_Zkd-W1eJI5jAE6xgNJq9JOkrYbN_tbntzS8bP3ekmo4_8WiOBEXOJD3zQI8ocXorxNIKLwvoXYYucGiaUAU3XdVR4jwIbzjUBAblRfgxS1sC010cUFoRKQkXDaC7lWBrhRzuX3BWq2Lba9QWjGi1_2UVb0vFEmt2SVvGFrbMXrE35it3HeGxfVsKj21-4Xokkryoc3jqzdMrNkVtRipa5Odq9nPRv6VjNm2fqYcrtY_Mdn0U5lzzjkmJ4_rT9VBvihLSizlBA4dP--kx3TIjqTyYjRFV2zArRNd8IqlFwy1OqHF1lPog4Q_P1FFNQ_uVKDlhCkd4m8fsSrXFlDr6I5AUTJU./download
    Resolving public.boxcloud.com (public.boxcloud.com)... 185.235.236.200
    Connecting to public.boxcloud.com (public.boxcloud.com)|185.235.236.200|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1559597 (1.5M) [application/octet-stream]
    Saving to: ‘personachat_all_sentences_test.jsonl’

    personachat_all_sen 100%[===================>]   1.49M  1.88MB/s    in 0.8s

    2020-03-22 23:47:22 (1.88 MB/s) - ‘personachat_all_sentences_test.jsonl’ saved [1559597/1559597]

    --2020-03-22 23:47:22--  https://nyu.box.com/shared/static/looczvct7ssh30x0gwqmvencple8xok9.pt
    Resolving nyu.box.com (nyu.box.com)... 185.235.236.197
    Connecting to nyu.box.com (nyu.box.com)|185.235.236.197|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /public/static/looczvct7ssh30x0gwqmvencple8xok9.pt [following]
    --2020-03-22 23:47:23--  https://nyu.box.com/public/static/looczvct7ssh30x0gwqmvencple8xok9.pt
    Reusing existing connection to nyu.box.com:443.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://nyu.app.box.com/public/static/looczvct7ssh30x0gwqmvencple8xok9.pt [following]
    --2020-03-22 23:47:23--  https://nyu.app.box.com/public/static/looczvct7ssh30x0gwqmvencple8xok9.pt
    Resolving nyu.app.box.com (nyu.app.box.com)... 185.235.236.199
    Connecting to nyu.app.box.com (nyu.app.box.com)|185.235.236.199|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://public.boxcloud.com/d/1/b1!VtbaRlDyF7vktjP1LUIcGOcNz2yDhBKTN_hjswBUfYMVYyX8S7kBxGFL2FByo9-x6ZyxELKOd1YLPYbzVCDlwZxf9c9ZmImbf4NVDfAg9LD0W5tjGk0iRowuCd6VWB_LvQv9rphgFgX92bRNEE-1sOe6Khs_VYEz_ciUJamdzLVPfzmNi3TqhlcuaPosKggQ9eOrpGpJ9Ilq0F_utKhqIkEgIhDbdxq2UslewdNdgFl-UKjpitqXduto4KxAKnP9nkSuKaJm3the1_BwaEySSIbBAlzM2Z-T5U3LE1w5Xr40t6-5ASxwpj-he-PLraXCJ6nqbJYUYJBIfXCJ9SlZSqbjx3nCmsXpLtz7ThjHE5N5tr7U32wustpw-w_dPRwbHOLcxFu0Z6h0RegOU8CdLOu70ngZV1F1OirmIUpR_e7cYb_b-JqNOku6tJbtDRYlokIteWBlvUCDPYBtfferdsqN_ZwsFBZ2ZbGhb7muBzfxyTpXm_salPbuda6m5rN66y-OkFjqVmVUEjmUN4e438xstcmXssjoOx4GZ1d4AvNiqcqgzWEJ3kFKshXp5eljuCSOXjjc5B2GZARgC_42y_mnPOdOdo10HWln3EpOHKNVwuaaDwo2E3MIsQ5pMjsTWJJKzBvul6usvVg5YZWxJaqMmyX0yFD9uyuW7-SjCD0d0d9c8BlpGAWnaxUXaKC7lpo1jGEPlbOL2UfNTwQiH9RYvwzjc7CvScXP6hDzxoK09GV_-wbezZX8YLeledA5U1vRStGsKGuNFPrb9QEjpx5Y-yRTBEJys23Ckn2TSfeTVlp2nRDowoDDUSLeXFxzAkErhSUuzt8fEUbl5DlTMNnbj-J8rwFZt6Zxe1gVdqbVuv59njsr54aw2Ebb3BElS1L1pL1vGPywYkvYzSIIp8CLWq1wBZJlyrO3O1ZVa95Rjv4f4HPTYF0d7CklGKSMjOOYRqrQH8mJla45Crg07Uy_29_ZE9zoa09RL-yriQvdlBeaQkU8woHe9mCMT7UsK6MFNoVe8c0CuBqKAnz7iUijfQyWOme8jeul3A28ckL1IZB_AzTzzsUGjlfX0-QVOszqW-d9BYmQ-cBnP8IlBoVQKLca1h76R2N7sQBhZnxtjxkApLlNzhtlFKGRBEiXj1oYvsn7WbKN0pWy60f2Vsqf9wIu_4DqFS6vqk5X97UImE98v_8XfnwxIpuZKzReYdeFh-G6HlvPN8ORKE8WMFynToPgBCPnmTHfVEHe8Ibd4G3EFP_oJ5H994wMoGE-5FFe-ow6pugyj9ZjeTcVBSAJuWp6b96xupfQDhrRL9SNhmmjUFtyGCXIrVcCVyqljgEEEq-9FgV8b28S60P9ZGe1xu_IIl77bMikswI8iCs5o2vnnpX0dFYX_F2V9M1K8vAP/download [following]
    --2020-03-22 23:47:24--  https://public.boxcloud.com/d/1/b1!VtbaRlDyF7vktjP1LUIcGOcNz2yDhBKTN_hjswBUfYMVYyX8S7kBxGFL2FByo9-x6ZyxELKOd1YLPYbzVCDlwZxf9c9ZmImbf4NVDfAg9LD0W5tjGk0iRowuCd6VWB_LvQv9rphgFgX92bRNEE-1sOe6Khs_VYEz_ciUJamdzLVPfzmNi3TqhlcuaPosKggQ9eOrpGpJ9Ilq0F_utKhqIkEgIhDbdxq2UslewdNdgFl-UKjpitqXduto4KxAKnP9nkSuKaJm3the1_BwaEySSIbBAlzM2Z-T5U3LE1w5Xr40t6-5ASxwpj-he-PLraXCJ6nqbJYUYJBIfXCJ9SlZSqbjx3nCmsXpLtz7ThjHE5N5tr7U32wustpw-w_dPRwbHOLcxFu0Z6h0RegOU8CdLOu70ngZV1F1OirmIUpR_e7cYb_b-JqNOku6tJbtDRYlokIteWBlvUCDPYBtfferdsqN_ZwsFBZ2ZbGhb7muBzfxyTpXm_salPbuda6m5rN66y-OkFjqVmVUEjmUN4e438xstcmXssjoOx4GZ1d4AvNiqcqgzWEJ3kFKshXp5eljuCSOXjjc5B2GZARgC_42y_mnPOdOdo10HWln3EpOHKNVwuaaDwo2E3MIsQ5pMjsTWJJKzBvul6usvVg5YZWxJaqMmyX0yFD9uyuW7-SjCD0d0d9c8BlpGAWnaxUXaKC7lpo1jGEPlbOL2UfNTwQiH9RYvwzjc7CvScXP6hDzxoK09GV_-wbezZX8YLeledA5U1vRStGsKGuNFPrb9QEjpx5Y-yRTBEJys23Ckn2TSfeTVlp2nRDowoDDUSLeXFxzAkErhSUuzt8fEUbl5DlTMNnbj-J8rwFZt6Zxe1gVdqbVuv59njsr54aw2Ebb3BElS1L1pL1vGPywYkvYzSIIp8CLWq1wBZJlyrO3O1ZVa95Rjv4f4HPTYF0d7CklGKSMjOOYRqrQH8mJla45Crg07Uy_29_ZE9zoa09RL-yriQvdlBeaQkU8woHe9mCMT7UsK6MFNoVe8c0CuBqKAnz7iUijfQyWOme8jeul3A28ckL1IZB_AzTzzsUGjlfX0-QVOszqW-d9BYmQ-cBnP8IlBoVQKLca1h76R2N7sQBhZnxtjxkApLlNzhtlFKGRBEiXj1oYvsn7WbKN0pWy60f2Vsqf9wIu_4DqFS6vqk5X97UImE98v_8XfnwxIpuZKzReYdeFh-G6HlvPN8ORKE8WMFynToPgBCPnmTHfVEHe8Ibd4G3EFP_oJ5H994wMoGE-5FFe-ow6pugyj9ZjeTcVBSAJuWp6b96xupfQDhrRL9SNhmmjUFtyGCXIrVcCVyqljgEEEq-9FgV8b28S60P9ZGe1xu_IIl77bMikswI8iCs5o2vnnpX0dFYX_F2V9M1K8vAP/download
    Resolving public.boxcloud.com (public.boxcloud.com)... 185.235.236.200
    Connecting to public.boxcloud.com (public.boxcloud.com)|185.235.236.200|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 63894321 (61M) [application/octet-stream]
    Saving to: ‘personachat_ffnn_lm.pt’

    personachat_ffnn_lm 100%[===================>]  60.93M  8.61MB/s    in 8.8s

    2020-03-22 23:47:35 (6.90 MB/s) - ‘personachat_ffnn_lm.pt’ saved [63894321/63894321]

    --2020-03-22 23:47:35--  https://nyu.box.com/shared/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt
    Resolving nyu.box.com (nyu.box.com)... 185.235.236.197
    Connecting to nyu.box.com (nyu.box.com)|185.235.236.197|:443... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: /public/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt [following]
    --2020-03-22 23:47:35--  https://nyu.box.com/public/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt
    Reusing existing connection to nyu.box.com:443.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://nyu.app.box.com/public/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt [following]
    --2020-03-22 23:47:36--  https://nyu.app.box.com/public/static/3jl9erctnvbefnczgmetkdil0xwpcxzb.pt
    Resolving nyu.app.box.com (nyu.app.box.com)... 185.235.236.199
    Connecting to nyu.app.box.com (nyu.app.box.com)|185.235.236.199|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://public.boxcloud.com/d/1/b1!CyLZtFf4saVvid5ZDvNRKfunVN0hx-Ls2eJmvuf44UDk5s1WCizYxz3mng51iSfOh7zdkvhl46QJxy6UtJN1MVLWlvHvVKHpZ5d4PJ51YVP1ww1NUC5KtpGeFEWKGmzOklmzzLjCvNgYuH-ArvTt3ogB_sjpfdJjBvN8WvaFnOwWUpKSYxxIg8DSgY34FYGlPbvV8-EOdsy3x3wAfKiWOpouBlj-jcMlhMUf0lSxRBSdapFhDtY8Z8miRS6IJJ2lY5zUV-hODqsf0hmgXrhvVsZJYwUH0yIc_a4lW6K18u7hLzy2qfjjdqIRirTdzm8wQIYvIQaIQGOjzqziB0-eV7hiKz7XiaiPwpGfHaCnBVp7vcA8q0ysolCCRmJorLGjozy1yhwYFWbxWllFxNnxWjxuPY5GUQlI_LIS1lw_oDEgsxVzeVtLjnDBU3G1gHNhaiDr-OicMYjDUwhZlHeUF2YgPzbVTYFMbO_vg0m5iwchS7pw9gXSwpXqLeHmrvrTx2ATkFAizX5jkokGU1hs0yLYINk3nxHJe2ftN7BONeYMjWY2nbxxCTDYXox6MHwY8aYOGQvJDJqKbCXbQNAH-uppP0LB7zw9VW7OGnLncHd_6JvhUo2akFXR9X0yGeLSRrYmYwolGk1NO1tV_cwBsVIxfOcribXbnK1fN0cz3OR3xp3Feyh49lCSQ0vM8mPMV7NtsxG4sCv8oE7WIOhqsEg9kct7S4adJ_GMXYPeKmh0z0owDYXgkVUAMIzPXiFEYKkz5zp31PjEY4QjM3PqUhrahe9KRoZsKEQzIQvS1j5EiUz2lfbUwAML4xvZv34EB7aVCMsr23cPNV0PNXpl0QK-8G57mcvH0gDPe_mQhOW9d59GxT8xd6LTK6_jINennOY_SPClvRGx2N36yFtVRR6xxjERhbtfuItYo2GvCdgQvJnFCQEIigW6_up2RjGjQor9JARHyPuBfvn7BQNEDUu2fvMXy2SucM44rL4bF9YOPjC0LlbMWt5Y9_7_khKS5sVCSDlmwmHpApgFRQtptn-TMTHYhmeicTT90w1Z_sTQUcSVqzOPIOiB30g3G58Ldvy07GB3MrXaHMRC5zYouzyigNXaCPqIw38fbqLBY2rUyFBmqktbm86pWrg1LipnFznx5cnEVU-ZGstxTK0VPADKeQ7RqjHo4npdDaN3RrmQIGu4FbK7TY8uzZsGCYaygbsItnFzSChN57x6AB0wym62lce1ivc4me2CCiBN_CigLITmQ4PtqrapVm3gqCjP1L6wr-sB9wnkY1dbxnoFONzqruuqKSpY6EBn-vqkPQE5bRoQtPPJbtS_YsSHV0AHbQaq6nD69R7K7Au1qQiEhojh5O4Fkc2pNDHnnlhlgzWT1tE3bRG9icY1-xKcC-O-iig./download [following]
    --2020-03-22 23:47:37--  https://public.boxcloud.com/d/1/b1!CyLZtFf4saVvid5ZDvNRKfunVN0hx-Ls2eJmvuf44UDk5s1WCizYxz3mng51iSfOh7zdkvhl46QJxy6UtJN1MVLWlvHvVKHpZ5d4PJ51YVP1ww1NUC5KtpGeFEWKGmzOklmzzLjCvNgYuH-ArvTt3ogB_sjpfdJjBvN8WvaFnOwWUpKSYxxIg8DSgY34FYGlPbvV8-EOdsy3x3wAfKiWOpouBlj-jcMlhMUf0lSxRBSdapFhDtY8Z8miRS6IJJ2lY5zUV-hODqsf0hmgXrhvVsZJYwUH0yIc_a4lW6K18u7hLzy2qfjjdqIRirTdzm8wQIYvIQaIQGOjzqziB0-eV7hiKz7XiaiPwpGfHaCnBVp7vcA8q0ysolCCRmJorLGjozy1yhwYFWbxWllFxNnxWjxuPY5GUQlI_LIS1lw_oDEgsxVzeVtLjnDBU3G1gHNhaiDr-OicMYjDUwhZlHeUF2YgPzbVTYFMbO_vg0m5iwchS7pw9gXSwpXqLeHmrvrTx2ATkFAizX5jkokGU1hs0yLYINk3nxHJe2ftN7BONeYMjWY2nbxxCTDYXox6MHwY8aYOGQvJDJqKbCXbQNAH-uppP0LB7zw9VW7OGnLncHd_6JvhUo2akFXR9X0yGeLSRrYmYwolGk1NO1tV_cwBsVIxfOcribXbnK1fN0cz3OR3xp3Feyh49lCSQ0vM8mPMV7NtsxG4sCv8oE7WIOhqsEg9kct7S4adJ_GMXYPeKmh0z0owDYXgkVUAMIzPXiFEYKkz5zp31PjEY4QjM3PqUhrahe9KRoZsKEQzIQvS1j5EiUz2lfbUwAML4xvZv34EB7aVCMsr23cPNV0PNXpl0QK-8G57mcvH0gDPe_mQhOW9d59GxT8xd6LTK6_jINennOY_SPClvRGx2N36yFtVRR6xxjERhbtfuItYo2GvCdgQvJnFCQEIigW6_up2RjGjQor9JARHyPuBfvn7BQNEDUu2fvMXy2SucM44rL4bF9YOPjC0LlbMWt5Y9_7_khKS5sVCSDlmwmHpApgFRQtptn-TMTHYhmeicTT90w1Z_sTQUcSVqzOPIOiB30g3G58Ldvy07GB3MrXaHMRC5zYouzyigNXaCPqIw38fbqLBY2rUyFBmqktbm86pWrg1LipnFznx5cnEVU-ZGstxTK0VPADKeQ7RqjHo4npdDaN3RrmQIGu4FbK7TY8uzZsGCYaygbsItnFzSChN57x6AB0wym62lce1ivc4me2CCiBN_CigLITmQ4PtqrapVm3gqCjP1L6wr-sB9wnkY1dbxnoFONzqruuqKSpY6EBn-vqkPQE5bRoQtPPJbtS_YsSHV0AHbQaq6nD69R7K7Au1qQiEhojh5O4Fkc2pNDHnnlhlgzWT1tE3bRG9icY1-xKcC-O-iig./download
    Resolving public.boxcloud.com (public.boxcloud.com)... 185.235.236.200
    Connecting to public.boxcloud.com (public.boxcloud.com)|185.235.236.200|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 67576905 (64M) [application/octet-stream]
    Saving to: ‘personachat_rnn_lm.pt’

    personachat_rnn_lm. 100%[===================>]  64.45M  10.8MB/s    in 17s

    2020-03-22 23:47:55 (3.69 MB/s) - ‘personachat_rnn_lm.pt’ saved [67576905/67576905]

## N-gram modeling as classification

In the previous n-gram language models lab, the probability of a word given a history ('prefix') was computed using n-gram **counts**.

In this lab, our goal will be the same: modeling the probability of variable length sequences, $$p(x_1,\ldots,x_T)=\prod_{t=1}^T p(x_t|x_{<t}),$$

by assuming that each word $x_t$ only depends on the preceding $n-1$ words, $$p(x_1,\ldots,x_T)=\prod_{t=1}^T p(x_t|x_{t-n+1},\ldots,x_{t-1}).$$

However, _instead of counts_, we will now use a **neural network** to **classify** each history, where each class is the next word.

[Diagram: modified from the [NLP Lecture Note](https://github.com/nyu-dl/NLP_DL_Lecture_Note/blob/master/lecture_note.pdf) by Kyunghyun Cho]

## Modeling chat utterances

We will build a **feed-forward neural n-gram model** to model chat utterances.

### 1. Prepare the data

To load and preprocess the data, we'll use utility functions from `data.py`.

```python
import data as data_utils
```

```python
datasets = data_utils.load_personachat('./')
dictionary = data_utils.Dictionary(datasets, include_valid=True)
```

    100%|██████████| 133176/133176 [00:17<00:00, 7738.66it/s]
    100%|██████████| 16181/16181 [00:03<00:00, 4905.39it/s]

Let's look at an example from the dataset (note that in addition to `train` we also have `valid`, `test` splits):

```python
index = 0
split = 'train'
encoded = dictionary.encode_token_seq(datasets[split][index])
decoded = dictionary.decode_idx_seq(encoded)

print("Dataset size %d (train), %d (valid), %d (test).\n" %
     (len(datasets['train']),
      len(datasets['valid']),
      len(datasets['test'])))
print(' '.join(datasets[split][index]))
print(f'\n word indices - {encoded}')
print(f'\n words - {decoded}')
```

    Dataset size 133176 (train), 16181 (valid), 15608 (test).

    i am doing great except for the allergies .

     word indices - [4, 5, 6, 7, 8, 9, 10, 11, 12]

     words - ['i', 'am', 'doing', 'great', 'except', 'for', 'the', 'allergies', '.']

Now we add `<bos>`, `<eos>`, and turn each **sequence** into **n-grams**:

```python
ngram_order = 5
tokenized_datasets = data_utils.tokenize_dataset(
    datasets, dictionary, ngram_order
)
ngram_datasets = data_utils.slice_into_ngrams(
    tokenized_datasets, ngram_order
)

for i in range(10):
    decoded = dictionary.decode_idx_seq(ngram_datasets[split][index+i])
    print(f'\n {i} - {decoded}')
```

     0 - ['<bos>', '<bos>', '<bos>', '<bos>', 'i']

     1 - ['<bos>', '<bos>', '<bos>', 'i', 'am']

     2 - ['<bos>', '<bos>', 'i', 'am', 'doing']

     3 - ['<bos>', 'i', 'am', 'doing', 'great']

     4 - ['i', 'am', 'doing', 'great', 'except']

     5 - ['am', 'doing', 'great', 'except', 'for']

     6 - ['doing', 'great', 'except', 'for', 'the']

     7 - ['great', 'except', 'for', 'the', 'allergies']

     8 - ['except', 'for', 'the', 'allergies', '.']

     9 - ['for', 'the', 'allergies', '.', '<eos>']

```python
decoded
```

    ['for', 'the', 'allergies', '.', '<eos>']

Finally, we create a **Pytorch** `Dataset` and `DataLoader` that can be used for training a neural network.

```python
from torch.utils.data import DataLoader

datasets = {}
dataloaders = {}
for split, dataset in ngram_datasets.items():
    dataset_ = data_utils.NgramDataset(dataset)
    datasets[split] = dataset_
    dataloaders[split] = DataLoader(
        dataset_,
        batch_size=2048,
        shuffle=(split == 'train'),
        collate_fn=data_utils.batchify
    )
```

### 2. Defining the model

#### How do we represent the input $x_t$?

Each $x_t$ is an **integer** (token id), and we transform it into a **vector** $f_{\text{embed}}(x_t)\in\mathbb{R}^d$ using an `Embedding` layer.

An embedding layer serves as a differentiable look-up table. Given an integer token id, it returns a vector representation which we call an **embedding**.

```python
import torch
from torch.nn import Embedding
# Embedding??
```

<div style="text-align:center"><span style="color:red; font-family:Georgia; font-size:2em;">#############################################################</div>

## Embedding understanding

Examples 1 :

        >>> # an Embedding module containing 10 tensors of size 3
        >>> embedding = nn.Embedding(10, 3)
        >>> # a batch of 2 samples of 4 indices each
        >>> input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
        >>> embedding(input)
        tensor([[[-0.0251, -1.6902,  0.7172],
                 [-0.6431,  0.0748,  0.6969],
                 [ 1.4970,  1.3448, -0.9685],
                 [-0.3677, -2.7265, -0.1685]],

                [[ 1.4970,  1.3448, -0.9685],
                 [ 0.4362, -0.4004,  0.9400],
                 [-0.6431,  0.0748,  0.6969],
                 [ 0.9124, -2.3616,  1.1151]]])

        >>> # example with padding_idx
        >>> embedding = nn.Embedding(10, 3, padding_idx=0)
        >>> input = torch.LongTensor([[0,2,0,5]])
        >>> embedding(input)
        tensor([[[ 0.0000,  0.0000,  0.0000],
                 [ 0.1535, -2.0309,  0.9315],
                 [ 0.0000,  0.0000,  0.0000],
                 [-0.1655,  0.9897,  0.0635]]])

Example 2 :

        >>> # FloatTensor containing pretrained weights
        >>> weight = torch.FloatTensor([[1, 2.3, 3], [4, 5.1, 6.3]])
        >>> embedding = nn.Embedding.from_pretrained(weight)
        >>> # Get embeddings for index 1
        >>> input = torch.LongTensor([1])
        >>> embedding(input)
        tensor([[ 4.0000,  5.1000,  6.3000]])

```python
>>> # an Embedding module containing 10 tensors of size 3
>>> embedding = Embedding(10, 3) # every number from 0-9 has an unique vector of size 3 as embedding
>>> # a batch of 2 samples of 4 indices each
>>> input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
>>> print(embedding(input))
>>> print(embedding(input).shape)
```

    tensor([[[ 0.2339, -1.4586,  0.9286],
             [ 1.2614, -0.8096,  0.9579],
             [-1.6218,  2.0938, -2.4359],
             [ 0.2904, -0.1651,  0.4570]],

            [[-1.6218,  2.0938, -2.4359],
             [ 1.4508,  1.4126,  1.1867],
             [ 1.2614, -0.8096,  0.9579],
             [ 0.3698, -0.4603, -0.0109]]], grad_fn=<EmbeddingBackward>)
    torch.Size([2, 4, 3])

```python
>>> # example with padding_idx
>>> embedding = Embedding(10, 3, padding_idx=0)
>>> input = torch.LongTensor([[0,2,0,5]])
>>> embedding(input)
```

    tensor([[[ 0.0000,  0.0000,  0.0000],
             [-0.1574,  0.2488,  0.0292],
             [ 0.0000,  0.0000,  0.0000],
             [-1.1507,  0.2455,  0.7681]]], grad_fn=<EmbeddingBackward>)

```python
>>> # FloatTensor containing pretrained weights
>>> weight = torch.FloatTensor([[1, 2.3, 3], [4, 5.1, 6.3]]) # we load our own embedding
>>> embedding = Embedding.from_pretrained(weight)
>>> # Get embeddings for index 1 and 0
>>> input = torch.LongTensor([1, 0, 1])
>>> embedding(input)
```

    tensor([[4.0000, 5.1000, 6.3000],
            [1.0000, 2.3000, 3.0000],
            [4.0000, 5.1000, 6.3000]])

<div style="text-align:center"><span style="color:red; font-family:Georgia; font-size:2em;">#############################################################</div>

```python
lookup = Embedding(
    num_embeddings=len(dictionary),
    embedding_dim=16,
    padding_idx=dictionary.get_id('<pad>')
)
```

```python
lookup.weight.size()
```

    torch.Size([20089, 16])

```python
input_ = dictionary.encode_token_seq(['hello', 'world'])
print(f'token ids: {[input_]}')

input_embedded = lookup(torch.tensor([input_], dtype=torch.long))
print(f'tokens embedding size: {input_embedded.size()}')

print(f"embedding for 'hello' ({input_[0]}):")
print(input_embedded[0, 0].data)

print(f"\nembedding for 'world' ({input_[1]}):")
input_embedded[0, 1].data
```

    token ids: [[179, 373]]
    tokens embedding size: torch.Size([1, 2, 16])
    embedding for 'hello' (179):
    tensor([-0.7419, -1.8968,  1.2170,  0.1180,  2.2437, -0.6516,  0.7949, -0.9582,
            -1.0196, -0.3526,  0.8856, -0.5736,  0.8464, -0.1362, -0.5174, -0.7914])

    embedding for 'world' (373):

    tensor([-0.6392, -0.5286, -0.8954, -2.1456,  0.8496,  1.5555,  1.1416, -0.9123,
             0.0053, -0.9957, -0.8532,  1.6187,  1.0987, -0.3596,  1.0180, -2.0651])

### Defining the neural network

There are three components:
1. `Embedding` $$\large f_{embed}:\text{vocab}\rightarrow \mathbb{R}^d$$
2. Hidden layer $$\large f_{hidden}: \mathbb{R}^{nd}\rightarrow \mathbb{R}^h$$
3. Projection $$\large f_{project}: \mathbb{R}^{h}\rightarrow \mathbb{R}^{|vocab|}$$

The conditional distribution is then: $$\large p(x_t|x_{t-1},\ldots,x_{t-n-1})=\text{softmax}(f_{project}(f_{hidden}(f_{embed}(x_{t-1},\ldots,x_{t-n-1}))).$$

We implement the hidden layer and projection with Pytorch `nn.Linear` layers:

```python
import torch
import torch.nn as nn

class NeuralNgramLM(nn.Module):
    def __init__(self, options):
        super().__init__()
        self.lookup = nn.Embedding(
            num_embeddings=options['num_embeddings'],
            embedding_dim=options['embedding_dim']
        )
        self.hidden_layer = nn.Linear(
            options['input_size'],  # input
            options['hidden_size'], # output
            bias=True
        )
        self.projection = nn.Linear(
            options['hidden_size'],   # input
            options['num_embeddings'] # output
        )

    def forward(self, inp):
        embedded = self.lookup(inp)
        embedded_timeflat = embedded.view(embedded.size(0), -1)
        hidden_out = self.hidden_layer(embedded_timeflat)
        hidden_out = torch.tanh(hidden_out)
        logits = self.projection(hidden_out)
        return logits
```

### 3. Train the model

#### Initialize the model, criterion (loss), and optimizer

```python
import os
import torch
import torch.nn as nn
import torch.optim as optim

load_pretrained = True

num_gpus = torch.cuda.device_count()
if num_gpus > 0:
    current_device = 'cuda'
else:
    current_device = 'cpu'

if load_pretrained:
    if not os.path.exists('personachat_ffnn_lm.pt'):
        raise EOFError('No model downloaded!')
    model_dict = torch.load(
        'personachat_ffnn_lm.pt',
        map_location=torch.device(current_device)
    )

    options = model_dict['options']
    model = NeuralNgramLM(options).to(current_device)
    model.load_state_dict(
        model_dict['model_dict']
    )

else:
    embedding_size = 256
    ngram_order = 4
    input_size = embedding_size * ngram_order
    hidden_size = 512

    options = {
        'num_embeddings': len(persona_dict),
        'embedding_dim': embedding_size,
        'input_size': input_size,
        'hidden_size': hidden_size,
    }
    model = NeuralNgramLM(options).to(current_device)

criterion = nn.CrossEntropyLoss(
    ignore_index=dictionary.get_id('<pad>'),
    reduction='sum'
)

model_parameters = [p for p in model.parameters() if p.requires_grad]
optimizer = optim.SGD(model_parameters, lr=0.01, momentum=0.99)
```

#### Training Loop

```python
plot_cache = []

if not load_pretrained:
    for epoch_number in range(10):
        avg_loss = -1

        # -- Training
        model.train()

        train_loss_cache = 0
        train_non_pad_tokens_cache = 0

        # == Iterate through batches from the dataset.
        for i, (inp, target) in enumerate(dataloaders['train']):
            optimizer.zero_grad()
            inp = inp.to(current_device)
            target = target.to(current_device)

            # == Forward pass.
            logits = model(inp)

            # == Computed (summed) loss.
            loss = criterion(logits.view(-1, logits.size(-1)), target.view(-1))
            train_loss_cache += loss.item()

            # == Compute the number of non `<pad>` tokens in the target.
            non_pad_tokens = target.view(-1).ne(dictionary.get_id('<pad>')).sum().item()

            # == Normalize the (summed) loss so that it is an average.
            loss /= non_pad_tokens
            loss.backward()
            optimizer.step()

            train_non_pad_tokens_cache += non_pad_tokens
            if i % 100 == 0:
                avg_loss = train_loss_cache/train_non_pad_tokens_cache
                print('Step {} avg train loss = {:.{prec}f}'.format(i, avg_loss, prec=4))

        # -- Validation
        valid_loss_cache = 0
        valid_non_pad_tokens_cache = 0

        model.eval()
        with torch.no_grad():
            non_pad_tokens = 0
            for i, (inp, target) in enumerate(dataloaders['valid']):
                inp = inp.to(current_device)
                target = target.to(current_device)

                # == Forward pass and loss
                logits = model(inp)
                loss = criterion(logits.view(-1, logits.size(-1)), target.view(-1))
                valid_loss_cache += loss.item()

                # == Normalize the loss
                non_pad_tokens = target.view(-1).ne(dictionary.get_id('<pad>')).sum().item()
                valid_non_pad_tokens_cache += non_pad_tokens

            avg_val_loss = valid_loss_cache / valid_non_pad_tokens_cache
            print('Validation loss after {} epoch = {:.{prec}f}'.format(epoch_number, avg_val_loss, prec=4))

        plot_cache.append((avg_loss, avg_val_loss))
```

```python
if load_pretrained:
    plot_cache = model_dict['loss_cache']
```

```python
import matplotlib.pyplot as plt
import numpy

epochs = numpy.array(list(range(len(plot_cache))))
plt.plot(epochs, [i[0] for i in plot_cache], label='Train loss')
plt.plot(epochs, [i[1] for i in plot_cache], label='Valid loss')

plt.legend()
plt.title('Loss curves')
plt.show()
```

### 4. Evaluate the model

#### Recap: Perplexity
Recall **perplexity** from the preceding lab:

\begin{align}
\large \text{ppl}(p, D) &\large = 2^{-\frac{1}{N_{total}}\log_2 p(D)}
\end{align}

where $D=\{(w_1,\ldots,w_{N_i})_i\}_{i=1}^M$ is a dataset of $M$ sequences with total length $N_{\text{total}}=\sum_{i}N_i$.

#### Computing perplexity

Our model's loss is the (negative) log probability of every token in the output sequence, which can be used to compute perplexity.

We define perplexity using the **base 2** logarithm. By default `torch.log` uses base `e`.

To **convert to base 2** we use: `log_2 p = log_e p / log_e 2`.

```python
import matplotlib.pyplot as plt
import numpy

epochs = numpy.array(list(range(len(plot_cache))))
plt.plot(epochs, [2**(i[0]/numpy.log(2)) for i in plot_cache], label='Train ppl')
plt.plot(epochs, [2**(i[1]/numpy.log(2)) for i in plot_cache], label='Valid ppl')

plt.legend()
plt.title('PPL curves')
plt.show()
```

```python
# Saving the model
if False:
    torch.save({
        'options': options,
        'loss_cache': plot_cache,
        'model_dict': model.state_dict()
    }, './persona_ffnn_lm.pt')
```

#### Conditional probability queries
We can now query the model for conditional probabilities:

\begin{align}
\large p(x_t|x_{t-1},\ldots,x_{t-4})
\end{align}

```python
import torch

def next_token_probabilities(prefix, token):
    inp = dictionary.encode_token_seq(prefix.split(' '))
    with torch.no_grad():
        model.eval()
        logits = model(
            torch.tensor([inp], dtype=torch.long).to(current_device)
        )
    token_id = dictionary.get_id(token)
    p_next_token = torch.softmax(logits, dim=-1)
    return p_next_token[0, token_id]

prefix = '<bos> i have a'

for w in ['dog', 'zebra', 'donut', 'velociraptor']:
    prob = next_token_probabilities(prefix, w)
    print('p( %s  | %s )\t= %.3E' % (w, prefix, prob))

```

    p( dog  | <bos> i have a )	= 7.468E-02
    p( zebra  | <bos> i have a )	= 1.473E-04
    p( donut  | <bos> i have a )	= 7.881E-06
    p( velociraptor  | <bos> i have a )	= 1.323E-06

And sort by the highest probability next-tokens:

$\large \texttt{sort}(p(\cdot|\underbrace{x_{<t}}_{\text{prefix}}))$

```python
def get_top_token_given_prefix(prefix, top=10):
    inp = dictionary.encode_token_seq(prefix.split(' '))
    with torch.no_grad():
        model.eval()
        logits = model(
            torch.tensor([inp], dtype=torch.long).to(current_device)
        )

    p_next_token = torch.softmax(logits, dim=-1)
    sorted_probs, sorted_token_ids = torch.topk(p_next_token[0], top)

    for i in range(sorted_token_ids.size(0)):
        print('p( %s  | %s )\t= %.5f' %
              (dictionary.decode_idx_seq([sorted_token_ids[i]])[0],
               prefix,
               sorted_probs[i])
        )

get_top_token_given_prefix('<bos> i have a')  # '<bos> the weather is'  '<bos> <bos> <bos> hello'
```

    p( dog  | <bos> i have a )	= 0.07468
    p( cat  | <bos> i have a )	= 0.04418
    p( lot  | <bos> i have a )	= 0.04310
    p( job  | <bos> i have a )	= 0.03759
    p( couple  | <bos> i have a )	= 0.03458
    p( few  | <bos> i have a )	= 0.02293
    p( black  | <bos> i have a )	= 0.01704
    p( little  | <bos> i have a )	= 0.01618
    p( hard  | <bos> i have a )	= 0.01604
    p( very  | <bos> i have a )	= 0.01478

# Recurrent neural network language model
Recall that in **language modeling**, we want to model the probability of variable length sequences, $$p(x_1,\ldots,x_T)=\prod_{t=1}^T p(x_t|x_{<t}).$$

*Unlike n-gram models*, we will now define a model that allows a **full history** $x_{<t}$.

To do so, we will use a function called a **recurrent neural network** to compute each distribution $p(x_t|x_{<t})$:
\begin{align}
h_t &= f^{\text{RNN}}_{\theta}(x_{t-1}, h_{t-1}) & \mathbb{R}^d\\
s_t &= Wh_t & \mathbb{R}^{\text{|vocab|}}\\
p_{\theta}(x_t|x_{<t}) &= \text{softmax}(s_t) & [0,1]^{\text{|vocab|}}
\end{align}

where $h_t$ is an internal (hidden) state of the model at time step $t$, and $x_t$ is an input at time step $t$.

In the simplest form used in practice, $f_{\theta}^{\text{RNN}}$ is defined as:
 $$f_{\theta}^{\text{RNN}}(x_{t-1}, h_{t-1}) = \text{tanh}(a_t)\\ a_t = b + W \cdot h_{t-1} + U \cdot x_{t-1},$$

but there are other variants (e.g. [GRU](https://arxiv.org/pdf/1406.1078.pdf), [LSTM](https://www.bioinf.jku.at/publications/older/2604.pdf)).

Similar to the n-gram neural network above, the goal at time $t$ is to **predict the next token $x_t$**.

### Pytorch implementation: `RNNCell`

```python
from torch.nn import RNNCell
# RNNCell??
```

```python
import torch
hidden_size = 16
embedding_dim = 8

rnn_cell = RNNCell(embedding_dim, hidden_size)
```

```python
hidden = torch.zeros(1, hidden_size)
print(hidden)
print(hidden.size())
```

    tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
    torch.Size([1, 16])

```python
random_input = torch.rand(1, embedding_dim)
print(random_input)
print(random_input.size())
```

    tensor([[0.6630, 0.1265, 0.1560, 0.5926, 0.8322, 0.5431, 0.9106, 0.5570]])
    torch.Size([1, 8])

```python
hidden = rnn_cell(random_input, hidden)
print(hidden)
print(hidden.size())
```

    tensor([[-0.2780,  0.0610, -0.1174, -0.2919, -0.4712,  0.0316, -0.2864, -0.0774,
             -0.4569,  0.2362,  0.1223,  0.3585, -0.0390, -0.1081,  0.4882, -0.1864]],
           grad_fn=<TanhBackward>)
    torch.Size([1, 16])

### Pytorch implementation: `RNN`

`RNNCell` is a building block of an RNN model. The Pytorch `RNN` model supports multi-layer networks by creating separate `RNNCell`s for every layer.

`RNN` can also process a whole sequence of data by iteratively applying `RNNCell`.

```python
from torch.nn import RNNBase, RNN
# RNNBase.__init__??
# RNN.__init__??
```

### 1. Define the model

```python
import torch.nn as nn

class RNNLanguageModel(nn.Module):
    def __init__(self, options):
        super().__init__()

        self.lookup = nn.Embedding(
            num_embeddings=options['num_embeddings'],
            embedding_dim=options['embedding_dim'],
            padding_idx=options['padding_idx']
        )
        self.rnn = nn.RNN(
            options['input_size'],
            options['hidden_size'],
            options['num_layers'],
            dropout=options['rnn_dropout'],
            batch_first=True
        )
        self.projection = nn.Linear(
            options['hidden_size'],
            options['num_embeddings']
        )

    def forward(self, token_ids):
        embeddings = self.lookup(token_ids)
        output, hidden = self.rnn(embeddings)
        logits = self.projection(output)
        return logits
```

### 2. Prepare the data

#### Dataset and Dataloader

See `data_utils.py` for the implementation.

```python
datasets_ = data_utils.load_personachat('./')
dictionary = data_utils.Dictionary(datasets_, include_valid=True)
datasets_ = data_utils.tokenize_dataset(datasets_, dictionary)

datasets = {}
for split, dataset in datasets_.items():
    datasets[split] = data_utils.TensoredDataset(dataset, dictionary.get_id('<pad>'))
```

    100%|██████████| 133176/133176 [00:18<00:00, 7382.82it/s]
    100%|██████████| 16181/16181 [00:03<00:00, 4807.32it/s]

- The **input** is now a **full sequence**.
- The **output** is the **next token** for each position of the input:

```python
inp, out = datasets['train'][0]

print("Input: \n\t%s\n\t(%s)" %
      (inp.data[0], dictionary.decode_idx_seq(inp.data[0])))

print("Output: \n\t%s\n\t(%s)" %
      (out.data[0], dictionary.decode_idx_seq(out.data[0])))
```

    Input:
    	tensor([ 0,  4,  5,  6,  7,  8,  9, 10, 11, 12])
    	(['<bos>', 'i', 'am', 'doing', 'great', 'except', 'for', 'the', 'allergies', '.'])
    Output:
    	tensor([ 4,  5,  6,  7,  8,  9, 10, 11, 12,  1])
    	(['i', 'am', 'doing', 'great', 'except', 'for', 'the', 'allergies', '.', '<eos>'])

#### Pytorch dataloader

```python
dataloaders = {}
batch_size = 128

for split, dataset in datasets.items():
    dataloaders[split] = DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=True,
        collate_fn=dataset.pad_collate_fn  # pads the sequences in the batch
    )
```

#### Padding to handle variable sequence lengths:

```python
inp, target = next(dataloaders['train'].__iter__())

print("Batch size %s" % str(inp.shape))
print("Batch element 1: \n\t%s\n\t(%s)" %
      (inp.data[0], ' '.join(dictionary.decode_idx_seq(inp.data[0]))))
print("Batch element 2: \n\t%s\n\t(%s)" %
      (inp.data[1], ' '.join(dictionary.decode_idx_seq(inp.data[1]))))
```

    Batch size torch.Size([128, 25])
    Batch element 1:
    	tensor([ 0,  4,  5, 44, 13, 21, 12,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
             2,  2,  2,  2,  2,  2,  2])
    	(<bos> i am just a kid . <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>)
    Batch element 2:
    	tensor([   0,  625, 3468,  122, 1791,   78,    4,  113,   86, 1178,   12,    2,
               2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
               2])
    	(<bos> everyone calls me mary and i live in colorado . <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>)

### 3. Train the model

#### Initializing model, criterion, and optimizer (similar to the feedforward model)

```python
import torch
import torch.nn as nn
import torch.optim as optim

load_pretrained = True

num_gpus = torch.cuda.device_count()
if num_gpus > 0:
    current_device = 'cuda'
else:
    current_device = 'cpu'

if load_pretrained:
    if not os.path.exists('personachat_rnn_lm.pt'):
        raise EOFError('Download pretrained model!')
    model_dict = torch.load(
        'personachat_rnn_lm.pt',
        map_location=torch.device(current_device)
    )

    options = model_dict['options']
    model = RNNLanguageModel(options).to(current_device)
    model.load_state_dict(model_dict['model_dict'])

else:
    embedding_size = 256
    hidden_size = 512
    num_layers = 3
    rnn_dropout = 0.3

    options = {
        'num_embeddings': len(dictionary),
        'embedding_dim': embedding_size,
        'padding_idx': dictionary.get_id('<pad>'),
        'input_size': embedding_size,
        'hidden_size': hidden_size,
        'num_layers': num_layers,
        'rnn_dropout': rnn_dropout,
    }
    model = RNNLanguageModel(options).to(current_device)

criterion = nn.CrossEntropyLoss(
    ignore_index=dictionary.get_id('<pad>'),
    reduction='sum'
)

model_parameters = [p for p in model.parameters() if p.requires_grad]
optimizer = optim.SGD(model_parameters, lr=0.001, momentum=0.999)
```

```python
model
```

    RNNLanguageModel(
      (lookup): Embedding(20089, 256, padding_idx=2)
      (rnn): RNN(256, 512, num_layers=3, batch_first=True, dropout=0.3)
      (projection): Linear(in_features=512, out_features=20089, bias=True)
    )

#### Training loop (same as the feedforward model)

```python
plot_cache = []

if not load_pretrained:
    for epoch_number in range(100):
        avg_loss = 0
        model.train()

        train_loss_cache = 0
        train_non_pad_tokens_cache = 0

        for i, (inp, target) in enumerate(persona_loaders['train']):
            optimizer.zero_grad()
            inp = inp.to(current_device)
            target = target.to(current_device)
            logits = model(inp)

            loss = criterion(logits.view(-1, logits.size(-1)), target.view(-1))
            train_loss_cache += loss.item()  # still sum here

            non_pad_tokens = target.view(-1).ne(persona_dict.get_id('<pad>')).sum().item()
            train_non_pad_tokens_cache += non_pad_tokens

            loss /= non_pad_tokens

            loss.backward()
            optimizer.step()

            if i % 100 == 0:
                avg_loss = train_loss_cache / train_non_pad_tokens_cache
                print('Step {} avg train loss = {:.{prec}f}'.format(i, avg_loss, prec=4))
                train_log_cache = []

        valid_loss_cache = 0
        valid_non_pad_tokens_cache = 0

        model.eval()
        with torch.no_grad():
            for i, (inp, target) in enumerate(persona_loaders['valid']):
                inp = inp.to(current_device)
                target = target.to(current_device)
                logits = model(inp)

                loss = criterion(logits.view(-1, logits.size(-1)), target.view(-1))
                valid_loss_cache += loss.item()  # still sum here
                non_pad_tokens = target.view(-1).ne(persona_dict.get_id('<pad>')).sum().item()

                valid_non_pad_tokens_cache += non_pad_tokens

            avg_val_loss = valid_loss_cache / valid_non_pad_tokens_cache

            print('Validation loss after {} epoch = {:.{prec}f}'.format(epoch_number, avg_val_loss, prec=4))

        plot_cache.append((avg_loss, avg_val_loss))

```

```python
if load_pretrained:
    plot_cache = model_dict['loss_cache']
```

### 4. Evaluation

```python
import matplotlib.pyplot as plt
import numpy

epochs = numpy.array(list(range(len(plot_cache))))
plt.plot(epochs, [i[0] for i in plot_cache], label='Train loss')
plt.plot(epochs, [i[1] for i in plot_cache], label='Valid loss')

plt.legend()
plt.title('Loss curves')
plt.show()
```

```python
import matplotlib.pyplot as plt
import numpy

epochs = numpy.array(list(range(len(plot_cache))))
plt.plot(epochs, [2**(i[0]/numpy.log(2)) for i in plot_cache], label='Train ppl')
plt.plot(epochs, [2**(i[1]/numpy.log(2)) for i in plot_cache], label='Valid ppl')

plt.legend()
plt.title('PPL curves')
plt.show()
```

### Conditional probability queries

We can query different probabilities:

$\large p(\underbrace{x_t}_{\text{token}}|\underbrace{x_{<t}}_{\text{prefix}})$

```python
import torch

def next_token_probabilities(prefix, token):
    inp = dictionary.encode_token_seq(prefix.split(' '))
    with torch.no_grad():
        model.eval()
        logits = model(
            torch.tensor([inp], dtype=torch.long).to(current_device)
        )

    token_id = dictionary.get_id(token)
    p_next_token = torch.softmax(logits[0, -1], dim=-1)
    return p_next_token[token_id]

prefix = '<bos> i have a'

for w in ['dog', 'zebra', 'donut', 'velociraptor']:
    prob = next_token_probabilities(prefix, w)
    print('p( %s  | %s )\t= %.3E' % (w, prefix, prob))

```

    p( dog  | <bos> i have a )	= 6.960E-02
    p( zebra  | <bos> i have a )	= 1.633E-04
    p( donut  | <bos> i have a )	= 1.039E-05
    p( velociraptor  | <bos> i have a )	= 2.267E-07

And sort by the highest probability next-tokens:

$\large \texttt{sort}(p(\cdot|\underbrace{x_{<t}}_{\text{prefix}}))$

```python
def get_top_token_given_prefix(prefix, top=10):
    inp = dictionary.encode_token_seq(prefix.split(' '))
    with torch.no_grad():
        model.eval()
        logits = model(
            torch.tensor([inp], dtype=torch.long).to(current_device)
        )

    p_next_token = torch.softmax(logits[0, -1], dim=-1)
    sorted_probs, sorted_token_ids = torch.topk(p_next_token, top)

    for i in range(sorted_token_ids.size(0)):
        print('p( %s  | %s )\t= %.5f' %
              (dictionary.decode_idx_seq([sorted_token_ids[i]])[0],
               prefix,
               sorted_probs[i])
        )

get_top_token_given_prefix('<bos> the weather is')
```

    p( great  | <bos> the weather is )	= 0.09953
    p( nice  | <bos> the weather is )	= 0.07859
    p( beautiful  | <bos> the weather is )	= 0.06185
    p( fun  | <bos> the weather is )	= 0.05039
    p( good  | <bos> the weather is )	= 0.04573
    p( awesome  | <bos> the weather is )	= 0.04308
    p( cool  | <bos> the weather is )	= 0.04212
    p( my  | <bos> the weather is )	= 0.03604
    p( a  | <bos> the weather is )	= 0.02892
    p( so  | <bos> the weather is )	= 0.02230

### Generation (Sampling)

Finally, we can generate sentences using the model's conditional distribution:

            context = [<bos>]
            until <eos> is generated:
                wt ~ p(wt | context)
                context += [wt]

Here, the `~` symbol stands for sampling from a categorical distribution.

```python
import torch

def sample(model, prefix, num_samples, temperature=1.0, max_len=50):
    inp = torch.tensor([
        dictionary.encode_token_seq(prefix.split(' '))
    ], dtype=torch.long).to(current_device).repeat([num_samples, 1])

    sampled = [prefix.split(' ') for _ in range(num_samples)]
    done = torch.tensor([False for _ in range(num_samples)])
    hidden = None

    with torch.no_grad():
        model.eval()
        for t in range(max_len):
            logits, hidden = step(model, inp, hidden)
            logits_ = (logits[:, -1, :] / temperature)
            inp = logits_.softmax(-1).multinomial(1)
            for i in range(num_samples):
                tok_ti = dictionary.get_token(inp[i, 0].item())
                if tok_ti == '<eos>' and not done[i]:
                    done[i] = True
                    sampled[i].append(tok_ti)
                elif not done[i]:
                    sampled[i].append(tok_ti)
                else:
                    pass
            if done.sum() == num_samples:
                break

    return sampled

def step(rnn, token_ids, hidden):
    embeddings = rnn.lookup(token_ids)
    output, hidden = rnn.rnn(embeddings, hidden)
    logits = rnn.projection(output)
    return logits, hidden

prefix = '<bos> i have a'

for temp in [1.0, 0.5, 0.1]:
    print("Temperature %.2f" % temp)
    samples = sample(model, prefix, 5, temperature=temp)
    for s in samples:
        print(' '.join(s))
    print()
```

    Temperature 1.00
    <bos> i have a cat named toto named after mah . <eos>
    <bos> i have a book named killswitch and trump my bf . are you married ? <eos>
    <bos> i have a huge belt play with globe tea listening to music . <eos>
    <bos> i have a lot of jokes to share . any hobbies ? <eos>
    <bos> i have a moped . <eos>

    Temperature 0.50
    <bos> i have a dog named toto . <eos>
    <bos> i have a lot of friends . <eos>
    <bos> i have a cat named cow . <eos>
    <bos> i have a dog . <eos>
    <bos> i have a lot of friends . <eos>

    Temperature 0.10
    <bos> i have a cat named cow . <eos>
    <bos> i have a cat named cow . <eos>
    <bos> i have a cat named cow . <eos>
    <bos> i have a cat named cow . <eos>
    <bos> i have a dog named toto . <eos>

```python
# Saving the model
if False:
    torch.save({
        'options': options,
        'loss_cache': plot_cache,
        'model_dict': model.state_dict()
    }, './static_files/persona_rnn_lm.pt')
```

## Large-scale Language Modeling: GPT-2

The language model above is relatively small in terms of the number of parameters, and is trained on a (very) small dataset.

Recently, researchers have trained **large-scale language models** on **large amounts of data**, resulting in improved language modeling and generation ability.

A representative example is **GPT-2** [[paper](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf)], [[blog](https://openai.com/blog/better-language-models/)]. We won't currently discuss the details of this model, but we can easily **use a pre-trained version to generate text**:

```python
!pip install transformers
```

    Requirement already satisfied: transformers in /opt/anaconda3/lib/python3.7/site-packages (2.5.1)
    Requirement already satisfied: requests in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (2.22.0)
    Requirement already satisfied: filelock in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (3.0.12)
    Requirement already satisfied: tokenizers==0.5.2 in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (0.5.2)
    Requirement already satisfied: boto3 in /opt/anaconda3/lib/python3.7/site-packages/boto3-1.9.246-py3.7.egg (from transformers) (1.9.246)
    Requirement already satisfied: tqdm>=4.27 in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (4.42.0)
    Requirement already satisfied: numpy in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (1.18.1)
    Requirement already satisfied: regex!=2019.12.17 in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (2019.8.19)
    Requirement already satisfied: sentencepiece in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (0.1.85)
    Requirement already satisfied: sacremoses in /opt/anaconda3/lib/python3.7/site-packages (from transformers) (0.0.38)
    Requirement already satisfied: idna<2.9,>=2.5 in /opt/anaconda3/lib/python3.7/site-packages (from requests->transformers) (2.8)
    Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /opt/anaconda3/lib/python3.7/site-packages (from requests->transformers) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /opt/anaconda3/lib/python3.7/site-packages (from requests->transformers) (2019.11.28)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /opt/anaconda3/lib/python3.7/site-packages (from requests->transformers) (1.24.2)
    Requirement already satisfied: botocore<1.13.0,>=1.12.246 in /opt/anaconda3/lib/python3.7/site-packages/botocore-1.12.246-py3.7.egg (from boto3->transformers) (1.12.246)
    Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /opt/anaconda3/lib/python3.7/site-packages/jmespath-0.9.5-py3.7.egg (from boto3->transformers) (0.9.5)
    Requirement already satisfied: s3transfer<0.3.0,>=0.2.0 in /opt/anaconda3/lib/python3.7/site-packages/s3transfer-0.2.1-py3.7.egg (from boto3->transformers) (0.2.1)
    Requirement already satisfied: joblib in /opt/anaconda3/lib/python3.7/site-packages (from sacremoses->transformers) (0.14.1)
    Requirement already satisfied: six in /opt/anaconda3/lib/python3.7/site-packages (from sacremoses->transformers) (1.12.0)
    Requirement already satisfied: click in /opt/anaconda3/lib/python3.7/site-packages (from sacremoses->transformers) (7.0)
    Requirement already satisfied: docutils<0.16,>=0.10 in /opt/anaconda3/lib/python3.7/site-packages (from botocore<1.13.0,>=1.12.246->boto3->transformers) (0.15.2)
    Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /opt/anaconda3/lib/python3.7/site-packages (from botocore<1.13.0,>=1.12.246->boto3->transformers) (2.8.1)

#### Load the model and tokenizer

```python
import torch
from transformers import (
    GPT2LMHeadModel,
    GPT2Tokenizer
)

device = torch.device('cpu') if not torch.cuda.is_available() else torch.device('cuda:0')

tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained("gpt2")
model.to(device);
```

    HBox(children=(FloatProgress(value=0.0, description='Downloading', max=1042301.0, style=ProgressStyle(descript…

    HBox(children=(FloatProgress(value=0.0, description='Downloading', max=456318.0, style=ProgressStyle(descripti…

    HBox(children=(FloatProgress(value=0.0, description='Downloading', max=224.0, style=ProgressStyle(description_…

    HBox(children=(FloatProgress(value=0.0, description='Downloading', max=548118077.0, style=ProgressStyle(descri…

#### Generate (Sampling)

We'll talk about other ways to generate later in the course.

```python
prefix = 'My favorite food is'

max_length = 100
num_samples = 5

inp = torch.tensor([tokenizer.encode(prefix)], device=device).repeat(num_samples, 1)

out = model.generate(
    input_ids=inp,
    do_sample=True,
    max_length=max_length
)

for i in range(num_samples):
    sample = tokenizer.decode(out[i])
    print(sample)
    print('---------------------------------------------------')
```

    My favorite food is not pizza…it is your meal!"

    "Wanna try me out? I should really start by asking for a discount. It's awesome!"

    "I'm here today with my buddy John to talk about this great recipe for Pizza Noodles."

    Thank you so much for stopping by! We wanted to introduce you to John for lunch and dinner.

    I like pizza. I've never had pizza in my life. The reason I have a
    ---------------------------------------------------
    My favorite food is the prawns from the island of Tanga. It's very cheap and easy to get for the price of one and then again for the rice with the traditional pork chop and chicken prawns. And they also tend to be great with pork or pork ribs if you can find them locally, that's very special.

    But how do you buy any special vegetables in Tonga?

    The pinyon sauce in Tonga is made with a bamboo straw and
    ---------------------------------------------------
    My favorite food is probably The Bistro, which is packed with the best, most savory food I've ever had. I love their sandwiches or the ones on the menu, especially if they're gluten free, they're filled with tasty things like tofu that you can use as sauce, you can use as bread, and I love the fact they provide you with something to spend more time with. The best thing about this place are the prices to pick up from: you can pay less than
    ---------------------------------------------------
    My favorite food is my family's favorite. It's so good!

    When it is easy to find some, it actually makes the whole family more fun. Our family goes to the park at Christmas time every day. I have our favorite food, it isn't as bad as the other foods but it is much more salty.

    We love this shop for all the wonderful things you can find there, including "Pizza". I haven't experienced anything that you might not know about it
    ---------------------------------------------------
    My favorite food is rice, and it tastes like rice. I use so many to make my dishes (they take a lot of time and work well), and I use lots more rice than I did last week on the restaurant's food roll. The menu has different flavors like chicken casserole, pasta, and seafood. The salad gets really spicy, and the bread is always greasy and greasy. I love its comfort food, and it doesn't bite.

    If you think you
    ---------------------------------------------------


# Please download the new class notes.
### Step 1 : Navigate to the directory where your files are stored.
Open a terminal.

Using `cd`, navigate to *inside* the ILAS_Python_for_everyone folder on your computer.

### Step 3 : Update the course notes by downloading the changes
In the terminal type:

>`git add -A`

>`git commit -m "commit"`

>`git fetch upstream`

>`git merge -X theirs upstream/master`

# Introduction to Data Structures and Imported Libraries (Pygame)

 Data Structures

 Lists

    __Indexing__

    Multiple User Inputs

    Manipulating Lists

       FindingLengthList

       SortingLists

       Removing an Item from a List

       Adding an Item to a List

       Changing a List Entry

    __Nested Data Structures__

    __Iterating over Lists__

    __Indexing when Iterating over Lists__

    `enumerate`

    __`zip`__

    __Example: Changing Position__

 Libraries

    __The Standard Library__

    __Packages__

       __Importing a Package__

       __Using Package Functions__

       __Reading Function Documentation__

       __Example: `math.pow`__

       Namespaces

       __Importing a Function__

    __Using Package Functions to Optimise your Code__

    __Data Structures as Function Arguments__

 __Stacking Functions__

 __Installing Pygame__

 Summary

 Test-Yourself Exercises

 Review Exercises

# Summary of Information Needed to Understand Today's Examples
For more information refer to the Primer Notebooks for this class __02ClassMaterial__DataStructures_Libraries.ipynb__

###### Data Structures

- Python has an extensive __standard library__ of built-in functions.
- More specialised libraries of functions and constants are available. We call these __packages__.
- Packages are imported using the keyword `import`
- The function documentation tells is what it does and how to use it.
- When calling a library function it must be prefixed with a __namespace__ is used to show from which package it should be called.
- The magic function `%timeit` can be used to time the execution of a function.

###### Libraries
- Python has an extensive __standard library__ of built-in functions.
- More specialised libraries of functions and constants are available. We call these __packages__.
- Packages are imported using the keyword `import`
- The function documentation tells is what it does and how to use it.
- When calling a library function it must be prefixed with a __namespace__ is used to show from which package it should be called.
- The magic function `%timeit` can be used to time the execution of a function.

### Lesson Goal

- Build a guessing game.
- Build the game 0 and Xs or tic-tac-toe.

### Fundamental programming concepts
 - Importing existing code to use in your program
 - Storing and representing data e.g. a grid with 0 and X in each grid cell

## Data Structures

In the last seminar we learnt to generate a range of numbers for use in control flow of  a program, using the function `range()`:

       for j in range(20):
           ...

Often we want to manipulate data that is more meaningful than ranges of numbers.

These collections of variables might include:
 - the results of an experiment
 - a list of names
 - the components of a vector
 - a telephone directory with names and associated numbers.

Python has different __data structures__ that can be used to store and manipulate these values.

Like variable types (`string`, `int`,`float`...) different data structures behave in different ways.

Today we will learn to use `list`s

A list is a container with compartments in which we can store data:

Example

If we want to store the names of students in a laboratory group,
rather than representing each students using an individual string variable, we could use a list of names.

```python
lab_group0 = ["Yukari", "Sajid", "Hemma", "Ayako"]
lab_group1 = ["Sara", "Mari", "Quang", "Sam", "Ryo", "Nao", "Takashi"]

print(lab_group0)
print(lab_group1)
```

    ['Yukari', 'Sajid', 'Hemma', 'Ayako']
    ['Sara', 'Mari', 'Quang', 'Sam', 'Ryo', 'Nao', 'Takashi']

This is useful because we can perform operations on lists such as:
 - checking its length (number of students in a lab group)
 - sorting the names in the list into alphabetical order
 - making a list of lists (we call this a *nested list*):

```python
lab_groups = [lab_group0, lab_group1]
```

### Example: Change in Position: (Representing Vectors using Lists)

__Vector:__ A quantity with magnitude and direction.

The position of a point in 2D space (e.g. the position of a character in a game), can be expressed in terms of horizontal (x) and vertical (y) conrdinates.

The movement to a new position can be expressed as a change in x and y.

This change is known as the velocity with which the point moves.

[Daniel Schiffman, The Nature of Code]

We can conveniently express the position $\mathbf{r}$ in matrix (or basis vector) form using the coefficients $x$ and  $y$:
$$
\mathbf{r} = [r_x, r_y]
$$

__...which looks a lot like a Python list!__

When we move a character in a game, we change it's position.

The change in position with each time-step is the __velocity__ of the character.

$$
\mathbf{v} = [v_x, v_y]
$$

To get the position at the next time step we simply add the x and y component of the veclocity to the x and y component of the initial position vector:

\begin{align}
      {\displaystyle {\begin{aligned}\
      \mathbf{r}(t=t+1)
      &=\mathbf{r} + \mathbf{v}\\
      &=[(r_x(t)+v_x),\;\;   (r_y(t)+v_y)] \\ \end{aligned}}}
\end{align}

For example, let's find the position at the next timestep where:
 - initial position,  $\mathbf{r} = [5, 2]$
 - velocity,  $\mathbf{v} = [3, 4]$

 [Daniel Schiffman, The Nature of Code]

```python
# Example: Change in Position

```

```python
# Example Solution: Change in Position

r = [5, 2]
v = [3, 4]

r = [r[0] + v[0],
     r[1] + v[1]]

print(r)
```

    [8, 6]

(Solution in 02_DataStructures_LibraryFunctions_SOLS.ipynb)

Arranging the code on seperate lines:
 - makes the code more readable
 - does not effect how the code works

Line breaks can only be used within code that is enclosed by at elast one set of brackets (), [].

__Check Your Solution:__

$ \mathbf{r} = [5, 2]$

 $ \mathbf{v} = [3, 4]$

\begin{align}
      {\displaystyle {\begin{aligned}\
      \mathbf{r} + \mathbf{v}
      &=[5, 2]+ [3, 4]\\
      &=[(5+3), \quad (2+4)] \\
      & = [8, 6] \end{aligned}}}
\end{align}

## Example : Tic-tac-toe
A list of lists can be used to represent a discrete set of positions the a player can occupy.

For example, we can construct a very primitive version of the game tic-tac-toe, using a list of lists and some of the other porgramming skills we have learned so far.

This example will combine the topics we have studied so far.

```python
# tic-tac-toe
```

```python
# tic-tac-toe
#
# Board positions:
#
# 00   01   02
# 10   11   12
# 20   21   22

# Example solution

# # use a for loop to set-up the 3x3 grid
board = []
for row in range(3):
    #board.append(['_', '_', '_'])
    board.append(['_']*3)

# display the output nicely
for row in board:
    for column in row:
        print(column, end='\t')
    # new line at end of row
    print()

# choose who goes first, X or 0
player = 'X'

# keep playing the game until told to quit
while(1):
    # use if and else to take turns at adding input from each player to the board
    if player == 'X':
        position = input('Player X, choose position: ')
        board[int(position[0])][int(position[1])] = 'X'
        player = '0'

    else:
        position = input('Player 0, choose position: ')
        board[int(position[0])][int(position[1])] = '0'
        player = 'X'

    #board[int(position[0])][int(position[1])] = player

    # display the output nicely
    for row in board:
        for column in row:
            print(column, end='\t')
        # new line at end of row
        print()

    print('\nnext turn \n')

    game_over = input('Game over?(Y/N): ')
    if game_over == 'Y':
        break
```

    _	_	_
    _	_	_
    _	_	_
    Player X, choose position: 20
    _	_	_
    _	_	_
    X	_	_

    next turn

    Game over?(Y/N): N
    Player 0, choose position: 21
    _	_	_
    _	_	_
    X	0	_

    next turn

    Game over?(Y/N): N
    Player X, choose position: 11
    _	_	_
    _	X	_
    X	0	_

    next turn

    Game over?(Y/N): N
    Player 0, choose position: 00
    0	_	_
    _	X	_
    X	0	_

    next turn

    Game over?(Y/N): N
    Player X, choose position: 02
    0	_	X
    _	X	_
    X	0	_

    next turn

    Game over?(Y/N): Y

## Libraries

One of the most important concepts in good programming is to reuse code and avoid repetitions.

Python, like other modern programming languages, has an extensive *library* of built-in functions.

These functions are designed, tested and optimised by the developers of the Python langauge.

We can use these functions to make our code shorter, faster and more reliable.

## The Standard Library

Python has a large standard library.

e.g. `print()` takes the __input__ in the parentheses and __outputs__ a visible representation.

They are listed on the Python website:
https://docs.python.org/3/library/functions.html

We could write our own code to find the minimum of a group of numbers

```python
x0 = 1
x1 = 2
x2 = 4

x_min = x0
if x1 < x_min:
    x_min = x1
if x2 < x_min:
    x_min = x2

print(x_min)
```

    1

However, it is much faster to use the build in function:

```python
print(min(1,2,4))
```

    1

The built-in functions can be found in (.py) files called 'modules'.

The files are neatly arranged into a system of __sub-packages__ (sub-folders) and __modules__ (files).

These files are stored on the computer you are using.

A quick google search for "python function to sum all the numbers in a list"...

https://www.google.co.jp/search?q=python+function+to+sum+all+the+numbers+in+a+list&rlz=1C5CHFA_enJP751JP751&oq=python+function+to+sum+&aqs=chrome.0.0j69i57j0l4.7962j0j7&sourceid=chrome&ie=UTF-8

...returns the function `sum()`.

`sum()` finds the sum of the values in a data structure.

```python
print(sum([1,2,3,4,5]))

print(sum((1,2,3,4,5)))

a = [1,2,3,4,5]
print(sum(a))
```

    15
    15
    15

The function `max()` finds the maximum value in data structure.

## Packages

The standard library tools are available in any Python environment.

More specialised libraries, called packages, are available for more specific tasks

e.g. solving trigonometric functions.

Packages contain functions and constants.

We install the packages to use them.

__Pygame__

For a large part of this course we will use functions from a package called `Pygame`.

`Pygame` is a set of Python modules designed for writing computer games, graphics and sound projects.

Instructions for how install pygame will be given later in today's seminar.

__math__

`math` is already installed and allows you to use convenient mathematical functions and operators.

A package is a collection of Python modules:
- a __module__ is a single Python file
- a __package__ is a directory of Python modules.
(It contains an __init__.py file, to distinguish it from folders that are not libraries).

The files that are stored on your computer when Pygame is installed:

https://github.com/pygame/pygame

### Importing a Package

To use an installed package, we  simply `import` it.

We only need to import the package once.

The `import` statement must appear before the use of the package in the code so all packages are usually imported at the start of the program.

        import numpy

We can then use variable and functions defined within that package by prefixing them with the name of the package:

```python
import math
```

After this, any constant, variable or function from the package can be used by prefixing it with the name of the package:

Any constant in `math` can be called as:

        `math.constant`.

Any function in `numpy` can be called as:

        `math.function()`

```python
# pi
print(math.pi)

x = 1

# Trigonometric functions e.g. cosine
y = math.cos(x)
print(y)
```

    3.141592653589793
    0.5403023058681398

## Using Package Functions.

Let's learn to use `math` functions in our programs...

```python
# Some examples math functions with their definitions (as given in the documentation)

x = 1

# Return the sine of x radians.
print(math.sin(x))

# Return the tangent of x radians.
print(math.tan(x))

# Return the inverse hyperbolic tangent of x.
print(math.atan(x))

```

    0.8414709848078965
    1.557407724654902
    0.7853981633974483

```python
x = 1

# Convert angle x from radians to degrees.
degrees = math.degrees(x)
print(degrees)

# Convert angle x from degrees to radians.
radians = math.radians(degrees)
print(radians)
```

    57.29577951308232
    1.0

## Reading Function Documentation

Online documentation can be used to find out:
- what to include in the () parentheses
- allowable data types to use as arguments
- the order in which arguments should be given

A google search for 'python math documentation' returns:

https://docs.python.org/3/library/math.html

(this list is not exhaustive).

### Try it yourself:

 Find a function in the Python math documentation (https://docs.python.org/3/library/math.html) that can be used to solve the following problem:

##### Return, $\left| x \right|$, the absolute value of x

Write your answer in the cell below:

```python
# Return the absolute value of x.
```

### Example : math.pow ($x^y$)
Documentation : https://docs.python.org/3/library/math.html#hyperbolic-functions

The documentation tells us the following information...

##### What arguments to input:
"math.pow(x, y)"

##### What the function returns:
"Return x raised to the power y."

##### The format of the returend argument:
math.pow() converts both its arguments to type float

## Using Package Functions to Optimise your Code
A fundamental purpose of using imported functions is to make your code shorter and neater.

For example, when designing a game it can be very useful to generate (pseudo) random numbers so that the challenges and problems for the user to solve are not identical every time the game is played.

Writing your own algorithm to generate random numbers is unecessarily time-consuming.

`random` is a python module that implements pseudo-random number generators for various distributions.

The documentation of the functions in this package can be found here:

https://docs.python.org/3/library/random.html#

Import random to use functions from this package:

```python
import random
```

Here are some examples of functions from `random`...

```python
# random.randint(a, b)
# Return a random integer N such that a <= N <= b.
random.randint(1, 15)

```

    13

```python
# random.sample(population, k)
# Return a k length list of unique elements chosen from the population sequence or set.
# Used for random sampling without replacement.
random.sample([1,2,3,4,5,6,7,8], 4)
```

    [5, 7, 8, 2]

Random numbers can be used to introduce an element of uncertainty to your programs.

This makes a game more intersting as the outcome may be different every time it is played.

For example, an adventure game may have a different outcome depending on a random variable.

```python
print("Inside the castle you see a giant spider.")
answer = input("Do you fight the spider?  (Yes/No) ")

if answer == 'Yes':
    print("you defeat the spider. \nYou win!")

else:
    print("The spider eats you. \nYou lose!")
```

    Inside the castle you see a giant spider.
    Do you fight the spider?  (Yes/No) no
    The spider eats you.
    You lose!

An adventure game where the outcome is random can be more interesting.

```python
print("Inside the castle you see a giant spider.")
answer = input("Do you fight the spider?  (Yes/No) ")

if answer == 'Yes':
    number = int(random.randint(0, 2))
    if number < 2:
        print("The spider defeats you. \nYou lose!")
    else:
        print("you defeat the spider. \nYou win!")

else:
    print("The spider eats you. \nYou lose!")
```

    Inside the castle you see a giant spider.
    Do you fight the spider?  (Yes/No) Yes
    The spider defeats you.
    You lose!

Random, computer generated values can also be used to build a game of rock, paper scissors where the user plays against the computer.

The code section below shows the case that the player choses *rock*.

```python

computer = int(random.randint(0, 2))
        if computer == 0:
            c_choice = "rock"
        elif computer == 1:
            c_choice = "paper"
        else:
            c_choice = "scissors"
```

The full version of the code is given in example program: Examples/02_RockPaperScissors.py

Try running this from the terminal by typing `python3 02_RockPaperScissors.py` from within the __PyEv2019/Examples__ directory.

## Installing Pygame
Install Pygame now. We will begin using this package in next week's class.

##### On Windows

1. Open the Anaconda Prompt from the terminal.

1. The window that opens will look like the command line. In the window type the following code then press 'Enter':
>`conda install -c anaconda pip`

1. When the installation completes type the following code then press 'Enter':
>`pip install pygame`

##### On mac

1. Open a terminal.

1. Type the following code then press 'Enter':
>`conda install -c anaconda pip`

1. When the installation completes type the following code then press 'Enter':
>`pip install pygame`

To check the installation has worked type:
>`import pygame`

in a Jupyter notebook cell and run the cell. If no error is generated you have installed pygame successfully.

# Review Exercises

Compete the exercise below.

Save your answers as .py files and email them to:

philamore.hemma.5s@kyoto-u.ac.jp

## Review Exercise : Guessing Game
__(A)__

Write a game that:
- chooses a random number between 1 and 10
- asks the user to guess what it is
- exits when the user guesses the right number

Use:
- a while loop
- a break statement (to break out of the while loop)
- a random number generator (from the package, `random`)

Example : The output from your game might look like this:

```
    I'm thinking of a random number between 1 and 10.
    Can you guess what it is?

    Guess what number I am thinking of: 5

    Guess what number I am thinking of: 2

    Guess what number I am thinking of: 1

    Guess what number I am thinking of: 9

    You win! I was thinking of 9.
```

*Hint: Remember that the function `input` returns a string even when a numerical value is entered.*

```python
# Guessing Game
```

__(B)__

Use `if` and `else` to generate a __clue__ for the player gets the answer wrong

The output from your game might look like this:
```
    I'm thinking of a random number between 1 and 10.
    Can you guess what it is?

    Guess what number I am thinking of: 1
    Too low.

    Guess what number I am thinking of: 5
    Too low.

    Guess what number I am thinking of: 9

    You win! I was thinking of 9.
    ```

```python
# Guessing Game with Clues
```

__(C)__

Use `break` to quit the game if the user makes three consecutive wrong guesses.

The output from your game might look something like this:

    I'm thinking of a random number between 1 and 10.
    Can you guess what it is? You have 3 guesses...

    Guess what number I am thinking of: 1
    Too low.

    Guess what number I am thinking of: 2
    Too low.

    Guess what number I am thinking of: 5

    You lose! I was thinking of 9.

```python
# Guessing game with maximum number of tries

```

```python
# Guessing game with maximum number of tries
# Example Solution

import random
import math

num = random.randint(1, 10)

print("I'm thinking of a random number between 1 and 10.")
print("Can you guess what it is?")

for i in range(3):
    guess = int(input("Guess what number I am thinking of: "))

    if guess == num:
        print(f"You win! I was thinking of {num}")
        break

    elif guess < num:
        print("Too low")

    else:
        print("Too high")

print(f"You lose! I was thinking of {num}.")
```

    I'm thinking of a random number between 1 and 10.
    Can you guess what it is?
    Guess what number I am thinking of: 8
    Too high
    Guess what number I am thinking of: 3
    Too low
    Guess what number I am thinking of: 5
    Too low
    You lose! I was thinking of 6.

```python

```