## Enable rendering OpenAI Gym environments from CoLab

In this assignemnt, We will use [OpenAI Gym](https://gym.openai.com/) for rendering game envionment for our agent to play and learn. It is possible and important to visualize the game your agent is playing, even on Colab. This section imports the necessary package and functions needed to generate a video in Colab. The video processing steps credit to [here](https://colab.research.google.com/drive/1flu31ulJlgiRL1dnN2ir8wGh9p7Zij2t).

```
!pip install gym pyvirtualdisplay > /dev/null 2>&1
!apt-get install -y xvfb python-opengl ffmpeg > /dev/null 2>&1
```

```
!apt-get update > /dev/null 2>&1
!apt-get install cmake > /dev/null 2>&1
!pip install --upgrade setuptools 2>&1
!pip install ez_setup > /dev/null 2>&1
!pip install gym[atari] > /dev/null 2>&1
```

    Requirement already up-to-date: setuptools in /usr/local/lib/python3.7/dist-packages (54.1.2)

Import openAI gym and define the functions used to show the video.

```
import gym
from gym.wrappers import Monitor
import glob
import io
import base64
from IPython.display import HTML
from pyvirtualdisplay import Display
from IPython import display as ipythondisplay

display = Display(visible=0, size=(1400, 900))
display.start()

"""
Utility functions to enable video recording of gym environment
and displaying it.
To enable video, just do "env = wrap_env(env)""
"""

def show_video():
  mp4list = glob.glob('video/*.mp4')
  if len(mp4list) > 0:
    mp4 = mp4list[0]
    video = io.open(mp4, 'r+b').read()
    encoded = base64.b64encode(video)
    ipythondisplay.display(HTML(data=''''''.format(encoded.decode('ascii'))))
  else:
    print("Could not find video")

def wrap_env(env):
  env = Monitor(env, './video', force=True)
  return env
```

Import other packages:

We will use Pytorch for building and learning our DQN network.

```
import torch
from torch import nn
import copy
from collections import deque
import random
from tqdm import tqdm
import matplotlib.pyplot as plt
```

## Run the game with random agent.

```
from torch import randint
from time import sleep

env = wrap_env(gym.make('CartPole-v0'))
reward_arr = []
episode_count = 20
for i in tqdm(range(episode_count)):
    obs, done, rew = env.reset(), False, 0
    env.render()
    while not done:
        A = randint(0, env.action_space.n, (1,))
        obs, reward, done, info = env.step(A.item())
        rew += reward
        sleep(0.01)
    reward_arr.append(rew)
print("average reward per episode :", sum(reward_arr) / len(reward_arr))
env.close()
show_video()
```

You can see that a random agent is having trouble balancing the CartPole, just like you. However, a difficult game for human may be very simple to a computer. Let's see how we can use DQN to train a agent.

## Experience Replay

The technique of experience replay was first proposed in to resolve temporal correlation in the input data by mixing recent experiences as well past experiences, essentially forcing the input to become independent and identically distributed (i.i.d.). It has been shown that this greatly stabilizes
and improves the DQN training procedure.

```
class ExperienceReplay(object):
      def __init__(self, length):
        self.experience_replay = deque(maxlen=length)

      def collect(self, experience):
        self.experience_replay.append(experience)
        return

      def sample_from_experience(self, sample_size):
        if len(self.experience_replay) < sample_size:
            sample_size = len(self.experience_replay)
        sample = random.sample(self.experience_replay, sample_size)
        state = torch.tensor([exp[0] for exp in sample]).float()
        action = torch.tensor([exp[1] for exp in sample]).float()
        reward = torch.tensor([exp[2] for exp in sample]).float()
        next_state = torch.tensor([exp[3] for exp in sample]).float()
        return state, action, reward, next_state

```

## Build our DQN Network

We will use a simple multi-layer neural network to learn the optimal actions. We will use Adam Optimizor and MSE loss for training.

```
class DQN_Network:

    def __init__(self, layer_size_list, lr, seed=1423):
        torch.manual_seed(seed)
        self.policy_net = self.create_network(layer_size_list)
        self.target_net = copy.deepcopy(self.policy_net)

        self.loss_fn = torch.nn.MSELoss()
        self.optimizer = torch.optim.Adam(self.policy_net.parameters(), lr=lr)

        self.step = 0
        self.gamma = torch.tensor(0.95).float()
        return

    def create_network(self, layer_size_list):
        assert len(layer_size_list) > 1

        layers = []
        for i in range(len(layer_size_list) - 1):
            linear = nn.Linear(layer_size_list[i], layer_size_list[i + 1])

            if i < len(layer_size_list) - 2:
              activation = nn.Tanh()
            else:
              activation = nn.Identity()

            layers += (linear, activation)
        return nn.Sequential(*layers)

    def load_pretrained_model(self, model_path):
        self.policy_net.load_state_dict(torch.load(model_path))

    def save_trained_model(self, model_path="cartpole-dqn.pth"):
        torch.save(self.policy_net.state_dict(), model_path)

```

## **[Your task]**: complete the function that chooses the next action

Choose next action based on **$\epsilon$-greedy**:

\begin{align}\text{where} \quad \mathcal{a_{t+1}} = \begin{cases}
     \text{argmax}_{a}Q(a, s)  & \text{with probability }: 1 - \epsilon, \text{exploitation}\\
     \text{Uniform}\{a_{1},...,a_{n}\} & \text{with probability}:   \epsilon, \text{exploration} \\
   \end{cases}\end{align}

```
def get_action(model, state, action_space_len, epsilon):
    # We do not require gradient at this point, because this function will be used either
    # during experience collection or during inference

    with torch.no_grad():
        Qp = model.policy_net(torch.from_numpy(state).float())
    Q_value, action = torch.max(Qp, axis=0)

    ## TODO: select action and action

    return
```

### **[Your task]**: complete the function that train the network for one step

Here, you can find an ``train`` function that performs a
single step of the optimization.

For our training update rule, the loss you are trying to minimize is:

\begin{align}\text{loss} = Q(s, a) - (r + \gamma \max_a Q(s', a))\end{align}

```
def train(model, batch_size):
    state, action, reward, next_state = memory.sample_from_experience(sample_size=batch_size)

    # TODO: predict expected return of current state using main network

    # TODO: get target return using target network

    # TODO: compute the loss
    loss =
    model.optimizer.zero_grad()
    loss.backward(retain_graph=True)
    model.optimizer.step()

    model.step += 1
    if model.step % 5 == 0:
        model.target_net.load_state_dict(model.policy_net.state_dict())

    return loss.item()
```

### **[Your task]**: Finish the training loop

In this part, you can play around with ```exp_replay_size```, ```episode```, ```epsilon``` and the "episodo decay" logic to train your model.

```
# Create the model
env = gym.make('CartPole-v0')
input_dim = env.observation_space.shape[0]
output_dim = env.action_space.n
agent = DQN_Network(layer_size_list=[input_dim, 64, output_dim], lr=1e-3)

# Main training loop
losses_list, reward_list, episode_len_list, epsilon_list = [], [], [], []

# TODO: try different values, it normally takes more than 6k episodes to train
exp_replay_size =
memory = ExperienceReplay(exp_replay_size)
episodes =
epsilon = 1 # episilon start from 1 and decay gradually.

# initiliaze experiance replay
index = 0
for i in range(exp_replay_size):
    obs = env.reset()
    done = False
    while not done:
        A = get_action(agent, obs, env.action_space.n, epsilon=1)
        obs_next, reward, done, _ = env.step(A.item())
        memory.collect([obs, A.item(), reward, obs_next])
        obs = obs_next
        index += 1
        if index > exp_replay_size:
            break

index = 128
for i in tqdm(range(episodes)):
    obs, done, losses, ep_len, rew = env.reset(), False, 0, 0, 0
    while not done:
        ep_len += 1
        A = get_action(agent, obs, env.action_space.n, epsilon)
        obs_next, reward, done, _ = env.step(A.item())
        memory.collect([obs, A.item(), reward, obs_next])

        obs = obs_next
        rew += reward
        index += 1

        if index > 128:
            index = 0
            for j in range(4):
                loss = train(agent, batch_size=16)
                losses += loss

    # TODO: add epsilon decay rule here!

    losses_list.append(losses / ep_len), reward_list.append(rew)
    episode_len_list.append(ep_len), epsilon_list.append(epsilon)

print("Saving trained model")
agent.save_trained_model("cartpole-dqn.pth")
```

## Last Step: evaluate your trained model!

First we can plot the reward vs. episode:

```
def plot_reward(r):
    plt.figure(2)
    plt.clf()
    plt.title('Result')
    plt.xlabel('Episode')
    plt.ylabel('Reward')
    plt.plot(r)

plot_reward(reward_list)
```

```
env = wrap_env(gym.make('CartPole-v0'))

input_dim = env.observation_space.shape[0]
output_dim = env.action_space.n
model_validate = DQN_Network(layer_size_list=[input_dim, 64, output_dim], lr=1e-3)
model_validate.load_pretrained_model("cartpole-dqn.pth")

reward_arr = []
for i in tqdm(range(300)):
    obs, done, rew = env.reset(), False, 0
    env.render()
    while not done:
        A = get_action(model_validate, obs, env.action_space.n, epsilon=0)
        obs, reward, done, info = env.step(A.item())
        rew += reward
        # sleep(0.01)

    reward_arr.append(rew)
print("average reward per episode :", sum(reward_arr) / len(reward_arr))
env.close()
show_video()
```


# Word Embeddings

- **Referent:** Maurice Vogel
- **Datum:** 05.12.2019

Das vorliegende Notebook soll sowohl einen ersten theoretischen als auch praktischen Einstieg in das Thema *Word Embeddings* bieten. Alle für diese Demo verwendeten Frameworks sind komplett Open-Source und in dem entsprechenden [Git-Repository](https://github.com/theThing92/word_embeddings_demo) verlinkt. Auch der hier gezeigte Source-Code darf frei verwendet werden (MIT-Lizenz).

```python
%matplotlib inline

import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
```

# Gliederung
1. Einleitung

    1.1. Semantik am Beispiel von Lewis Carrols "Jabberwocky"

    1.2. Distributionelle Hypothese

2. theoretischer Hintergrund

    2.1. lexikalische Semantik

    2.2. Vektorsemantik

    2.3. Embeddings

3. Semantik über Sparse Vectors

    3.1. Bag-of-Words (BOW) / Term-Dokument-Matrix

    3.2. Term-Term-Matrix / Wort-Wort-Matrix

4. Gewichtung von frequenzbasierten Vektoren

    4.1. TF-IDF

    4.2. Pointwise Mutual Information

    4.3. Anwendungsfelder

5. Ähnlichkeitsbestimmung im Vektorraum

6. Semantik über Dense Vectors

    6.1. word2vec

    6.2. weitere Verfahren

    6.3. Contextualized String Embeddings

7. Diskussion

## 1. Einleitung

**Lernziele**
- Was ist die Grundidee hinter vektorraum-basierten Sprachmodellen, wie z.B. Word Embeddings?

### 1.1. Semantik am Beispiel von Lewis Carrols "Jabberwocky"

Für einen exemplarischen Einstieg in die Thematik betrachten Sie bitte das unten stehende Gedicht ["Jabberwocky" aus Lewis Carrols "Alice hinter den Spiegeln" (1871)](https://de.wikipedia.org/wiki/Jabberwocky).

**Aufgaben**:
1. Was ist die Bedeutung des Wortes ***Jabberwock***?
2. Wie kann die Semantik dieses unbekannten Wortes erschlossen werden?

>Twas brillig, and the slithy toves

>Did gyre and gimble in the wabe;

>All mimsy were the borogoves,

>And the mome raths outgrabe.

>
>Beware the **Jabberwock**, my son!

>The jaws that bite, the claws that catch!

>Beware the Jubjub bird, and shun

>The frumious Bandersnatch!

>
>He took his vorpal sword in hand:

>Long time the manxome foe he sought

>So rested he by the Tumtum tree,

>And stood awhile in thought.

>
>And as in uffish thought he stood,

>The **Jabberwock**, with eyes of flame,

>Came whiffling through the tulgey wood,

>And burbled as it came!

>
>One, two! One, two! And through and through

>The vorpal blade went snicker-snack!

>He left it dead, and with its head

>He went galumphing back.

>
>And hast thou slain the **Jabberwock**?

>Come to my arms, my beamish boy!

>O frabjous day! Callooh! Callay!

>He chortled in his joy.

>
>Twas brillig, and the slithy toves

>Did gyre and gimble in the wabe;

>All mimsy were the borogoves,

>And the mome raths outgrabe.

**Lösung:**

*Aufgabe 1*

- Semantik ~ "gefährliches, drachenähliches Fabelwesen mit Zähnen, Klauen und feurigen Augen"
*Abb. 1: künstlerische Darstellung des "Jabberwock" von [John Tenniel](https://de.wikipedia.org/wiki/John_Tenniel)*

*Aufgabe 2*

Die Bedeutung eines unbekannten Wortes kann über den umgebenden Kontext erschlossen werden. Das Wort ***Jabberwock*** taucht vermehrt mit Wörter auf, die auf Gefahr hindeuten, z.B.:
  - "**Beware** the Jabberwock, my son!\ The **jaws** that **bite**, the **claws** that catch!"
  - "The Jabberwock, with **eyes of flame**,\ Came **whiffling** through the tulgey wood,\ And **burbled** as it came!"
  - ...

Zudem ähnelt die Handlungsstruktur des Gedicht denen von klassischen Heldensagen, wie z.B. der Nibelungensaga, in denen ein Held loszieht, um einen Drachen zu erschlagen, sodass wir durch Analogiebildung feststellen können, dass ein ***Jabberwock*** ein drachenähnliches Fabelwesen sein muss. Hierbei kommt allerdings ein deutlich komplexeres Weltwissen zum Tragen, das über ein reines "Wortkontext-Wissen" hinausgeht und demzufolge auch schwieriger zu modellieren wäre.

### 1.2. Distributionelle Hypothese
Ausgehend von der oben dargestellten Beobachtung entwickelte Wittgenstein in der (Sprach-)Philosophie folgende Hypothese zur Frage, wie die Semantik eines Wortes definiert werden könne:

> “the meaning of a word is its use in the language” (Wittgenstein,
1953)

Diese gebrauchsorientierte Wortsemantikdefinition grenzt sich insbesondere zu logisch-formalen Ansätzen ab, welche Wittgenstein als wenig praktikabel zur Darstellung von natürlicher Sprache erachtete (Jurafsky & Martin, 2019).

Dieser Grundgedanke wurde in den 1950ern von verschiedenen Linguisten (Joos, 1950; Harris,1954; Firth, 1957) zur sogenannten **Distributionelle Hypothese** (*distributional hypothesis*) 'erweitert':

> "\[D\]efine a word by its **environment or distribution** in language use. A word’s
distribution is the set of contexts in which it occurs, the neighboring words or grammatical
environments. The idea is that two words that occur in very similar distributions
(that occur together with very similar words) are likely to have the same
meaning." (Jurafsky & Martin, 2019)

## 2. Theoretischer Hintergrund

**Lernziele**
- Wie kann die Bedeutung zwischen Wörtern beschrieben werden?
- Wie kann die Semantik eines Wortes als Vektor dargestellt werden?
- Was sind Embeddings in diesem Kontext?

### 2.1. lexikalische Semantik

In der Forschung zur lexikalischen Semantik sind verschiedene Termini gebräuchlich, welche die semantischen Eigenschaften von Wörtern sowie ihre Relationen zueinander im Lexikon zu fassen suchen.

**Aufgabe**
3. Welche Termini kennen sie zur Beschreibung dieser Eigenschaften und "Anordnungsrelationen"? Können Sie entsprechende Beispiele nennen? (vgl. Jurafsky & Martin, 2019)

**Lösung (Auswahl)**

*Aufgabe 3*

- Lemma - Wortform

  - Mann - Männer (Flexion)

  - Mann - männlich (Derivation)

  - Mann - Männerverein (Komposition)

  - ...

- lexikalische Relationen

  - Synonymie

  - Polysemie

  - ...

- Ähnlichkeit

  - vgl. SimLex-999 Datensatz (Hill et al., 2015)

- Verwandschaft / Bezogenheit / Assoziation
  - z.B.: Kaffee - Becher

  - semantische Felder / Wortfelder

    - Topic-Modelle

      - [Latent Semantic Indexing / Latent Semantic Analyis (LSI/LSA)](lsa.colorado.edu/papers/JASIS.lsi.90.pdf)

      - [Latent Dirichlet Allocation (LDA)](http://www.jmlr.org/papers/volume3/blei03a/blei03a.pdf)

      - [Hierarchical Dirichlet Process (HDP)](https://people.eecs.berkeley.edu/~jordan/papers/hdp.pdf)

      - ...

    - semantische Frames und Rollen

      - >"A semantic frame is a set of words that denote perspectives or participants in a particular type of event."
 (Jurafsky & Martin, 2019)

- Affektive Bedeutung / Konnotation

  - *mögliche* Beschreibungsebenen: positiv - negativ - neutral - ...

    - Onkel Doktor - Halbgott in Weiß - Arzt - ...

  - Sentiment

    - Sentiment Analyis

Eine der frühen Arbeiten zur Beschreibung der affektiven Wortbedeutung (Osgood et al., 1957) bildet hierbei die theoretische Grundlage für die Darstellung von Wörtern als Vektoren.

### 2.2. Vektorsemantik
**Hintergrund**

Osgood et al. (1957, zitiert nach Jurafsky & Martin, 2019) definieren die affektive Bedeutung eines Wortes nach folgenden **drei** Dimensionen:

> **valence**: the pleasantness of the stimulus

> **arousal**: the intensity of emotion provoked by the stimulus

> **dominance**: the degree of control exerted by the stimulus

Die affektive Wortbedeutung von mehreren Wörtern könnte also tabellarisch wie folgt beschrieben werden:

```python
import pandas as pd
# Erzegung einer Tabelle mit den Wörtern als Index sowie deren Ausprägungen
# für die drei o.g. Dimensionen "Valence", "Arousal" und "Dominance"
data_osgood = {"courageous":[8.05, 5.5, 7.38],
               "music":[7.67, 5.57, 6.5],
               "heartbreak":[2.45, 5.65, 3.58],
               "cub":[6.71, 3.95, 4.24],
               "life":[6.68, 5.59, 5.89]}
df_osgood = pd.DataFrame.from_dict(data_osgood, orient="index", columns =["Valence", "Arousal", "Dominance"])
df_osgood
```

      Valence
      Arousal
      Dominance

      courageous
      8.05
      5.50
      7.38

      music
      7.67
      5.57
      6.50

      heartbreak
      2.45
      5.65
      3.58

      cub
      6.71
      3.95
      4.24

      life
      6.68
      5.59
      5.89

Hierbei fällt auf, dass man diese Information nicht nur tabellarisch, sondern auch als Datenmatrix *X* der einzelnen Vektoren (*v1* - *v5*) dargestellt werden könnte, sodass die Wortsemantik als Vektor in einem drei-dimensionalen Raum codierbar wäre.

```python
matrix_osgood = df_osgood.values
matrix_osgood
```

    array([[8.05, 5.5 , 7.38],
           [7.67, 5.57, 6.5 ],
           [2.45, 5.65, 3.58],
           [6.71, 3.95, 4.24],
           [6.68, 5.59, 5.89]])

```python
rows_osgood, cols_osgood = matrix_osgood.shape
print(f"Anzahl Zeilen (n): {rows_osgood}")
print(f"Anzahl Spalten (m): {cols_osgood}")
```

    Anzahl Zeilen (n): 5
    Anzahl Spalten (m): 3

```python
%matplotlib inline
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(20,14))
ax = fig.gca(projection='3d')
ax.set_xlabel('Valence')
ax.set_ylabel('Arousal')
ax.set_zlabel('Dominance')
ax.scatter3D(matrix_osgood[:,0], matrix_osgood[:,1], matrix_osgood[:,2])
for i,v in enumerate(matrix_osgood):
    ax.text(matrix_osgood[i,0],matrix_osgood[i,1], matrix_osgood[i,2], f"{df_osgood.index[i]}")

```

Osgood et al. (1957) stellen hierbei die Semantik eines Wortes auf Grundlage von **vorab definierten Merkmalsdimensionen** (*feature engineering*) dar. Neuere computerlinguistische Ansätze "überspringen" diesen Schritt der expliziten Merkmalskodierung, indem relevante semantische Merkmale automatisch aus unannotierten Text-Korpora gelernt werden können. Dies wird als *feature learning / feature extraction* bezeichnet (später mehr dazu). Hierbei wird hauptsächlich der umgebende Kontext eines Wortes berücksichtigt, um auf dieser Basis eine Wortsemantik erlernen zu können.

In diesem Sinne vereinen diese Algorithmen folgende zwei Intuition:
1. Wörter sind als Punkte/Vektoren in einem mehrdimensionalen Raum darstellbar
2. die Bedeutung eines Wortes erschließt sich durch seinen Kontext (Distributionelle Hypothese)

**Hinweis:** Obwohl ich aktuell von Wörtern spreche, werden wir bei späteren Verfahren sehen, dass diese nicht nur die Semantik einzelner Wörter codieren können, sondern beliebige Sequenzen als Input nehmen können (z.B. Phrasen, Sätze etc.).

### 2.3. Embeddings

Aus den in [2.2.](#2.2.-Vektorsemantik) vorgestellten Zusammenhängen kann nun der Begriff Embedding defininert werden:

**Definition**
>The idea of vector semantics is thus to represent a word as a point in some multidimensional
>semantic space. Vectors for representing words are generally called
>**embeddings**, because the word is embedded in a particular vector space.

> (Jurafsky & Martin, 2019)

Die einzelnen Algorithmen zur Generierung dieser Embeddings arbeiten im Detail sehr unterschiedlich, gemein ist ihnen allen jedoch, dass sie **immer** den umgebenden Kontext ihres Inputs (Wörter, Sätze, Phrasen, Paragraphen, Dokumente etc.) für die Generierung der gewünschten 'Ziel-Embeddings' nutzen. Hierfür müssen die Modelle zunächst auf einer ausreichenden Menge an Texten trainiert werden. Anschließend können auch neue oder unbekannte Daten durch das Modell in den entsprechenden Vektorraum transformiert/eingebettet werden. Das Resultat dieser Einbettung/Transformation ist eine semantische Repräsentation für einen Input, von dem wir kein zuvor **keine** Information über dessen Semantik hatten.

**Hinweis:** Der resultierende semantische Raum bzw. die semantische Anordnung der Vektoren in diesem Raum lässt sich jedoch aktuell nur relativ schwierig (systematisch) unter die in [2.1.](#2.1.-lexikalische-Semantik) dargestellten Konzepte fassen. Wir können zwar bestimmte semantische Phänomene in diesem Vektorraum beobachten (später mehr dazu) und durch Embeddings konnte die Performanz von NLP-Systemen für sehr verschiedene Aufgabenstellungen gesteigert werden, allerdings besteht noch ein deutlicher Forschungsbedarf mit Blick auf die Transparenz und Erklärbarkeit dieser Modelle (vgl. Jurafsky & Martin, 2019).

Vereinfacht gesagt: Embeddings funktionieren, wir wissen nur nicht genau warum ;)

Embeddings zu Sprachdaten können hierbei grundsätzlich auf zwei Arten codiert werden
1. *sparse* ('dünnbesetzt')
2. *dense* ('dichtbesetzt')

Da die Codierung von Sprachdaten als *sparse vector* das - forschungshistorisch betrachtet - ältere Verfahren darstellt, soll zunächst diese Datenrepräsentation erläutert werden.

## 3. Semantik über Sparse Vectors
**Lernziele:**
- Was ist die BOW-Repräsenation? Warum ist sie ein Beispiel für einen *sparse vector* bzw. eine *sparse matrix*?
- Was ist der Unterschied zwischen einer Term-Dokument- und einer Term-Term-Matrix?

### 3.1. Bag-of-Words (BOW) / Term-Dokument-Matrix

Eine der ersten Ansätze zur Repräsentation der Bedeutung von unstrukturierten Textdaten kommt aus dem Bereich des **Information Retrieval (IR)**. Die Semantik eines Textes wird in den sogenannten **Bag-of-Words-Modellen (BOW)** als Menge der Vorkommenshäufigkeit von **ungeordneten** Termen (~ Wörter/Tokens) innerhalb jedes Dokuments erfasst. Über alle Dokumente eines Korpus hinweg kann somit eine Kookkurenz-Matrix aller Terme für alle Dokumente erstellt werden. Diese Matrix bezeichnet man als **Term-Dokument-Matrix oder BOW** . Hierbei gilt:

- Anzahl Spalten = |V| (V=Vokabular)
- Anzahl Zeilen = |D| (D=Menge der Dokumente)

Ein Beispiel für eine Term-Dokument-Matrix findet sich in der nächsten Zelle.

```python
# Beispiel für Term-Dokument-Matrix
data_shakespeare = {"battle":[1, 0, 7, 13],
                    "good":[114, 80, 62, 89],
                    "fool":[36, 58, 1, 4],
                    "wit":[20, 15, 2, 3]}
df_shakespeare = pd.DataFrame.from_dict(data_shakespeare, orient="index",
                                        columns =["As You Like It", "Twelth Night", "Julius Caesar", "Henry V"])
df_shakespeare
```

      As You Like It
      Twelth Night
      Julius Caesar
      Henry V

      battle
      1
      0
      7
      13

      good
      114
      80
      62
      89

      fool
      36
      58
      1
      4

      wit
      20
      15
      2
      3

Das o.g. BOW kann wie folgt aus (tokenisierten) Dokumenten erzeugt werden:

>**Vorgehen**
>1. Bestimme |V|

>   - Anzahl Spalten der Matrix

>2. Bestimme |D|

>   - Anzahl Zeilen der Matrix

>3. Bestimme die Termfrequenz jedes Terms in jedem Dokument

>4. Codiere die Termfrequenz pro Dokument für **alle gleichen Terme** immer in derselben Spalte

>   - *Hinweis: Meist wird chronologisch durch die Dokumente und Terme iteriert, um zu bestimmen, in welcher Dimension welcher Term codiert werden soll. Prinzipiell ist die Dimensionscodierung aber arbiträr.*

**Aufgabe**
4. Erstellen Sie ein BOW für das unten angegebene Korpus *corpus*. Das Korpus liegt tokenisiert vor.

```python
# Erstellung eines simplen Spielzeug-Korpus
from pprint import pprint
from nltk.tokenize import RegexpTokenizer
tokenizer = RegexpTokenizer(r'\w+')

# Daten von hier:
# https://radimrehurek.com/gensim/auto_examples/tutorials/run_word2vec.html#sphx-glr-auto-examples-tutorials-run-word2vec-py
string_doc1 = "John likes to watch movies. Mary likes movies too."
string_doc2 = "John also likes to watch football games. Mary hates football."

doc1_tokenized = tokenizer.tokenize(string_doc1)
doc2_tokenized = tokenizer.tokenize(string_doc2)

corpus = [doc1_tokenized,doc2_tokenized]
pprint(corpus,width=100)
```

    [['John', 'likes', 'to', 'watch', 'movies', 'Mary', 'likes', 'movies', 'too'],
     ['John', 'also', 'likes', 'to', 'watch', 'football', 'games', 'Mary', 'hates', 'football']]

```python

```

**Lösung (chronologisch)**

d1_bow = [1, 2, 1, 1, 2, 1, 1, 0, 0, 0, 0]

d2_bow = [1, 1, 1, 1, 0, 1, 0, 1, 2, 1, 1]

```python

```

```python
# BOW in Gensim

# Erzeugung eines Dictionary, welches für jeden Term die Codierungsdimension speichert
from gensim.corpora import Dictionary

dictionary = Dictionary(corpus)
print(dictionary)
```

    2019-12-12 16:29:53,382 : INFO : adding document #0 to Dictionary(0 unique tokens: [])
    2019-12-12 16:29:53,383 : INFO : built Dictionary(11 unique tokens: ['John', 'Mary', 'likes', 'movies', 'to']...) from 2 documents (total 19 corpus positions)

    Dictionary(11 unique tokens: ['John', 'Mary', 'likes', 'movies', 'to']...)

```python
# Hinweis: Die Codierung der Elemente erfolgt hierbei
# **nicht chronologisch**
pprint({v:k for k,v in dictionary.token2id.items()})
```

    {0: 'John',
     1: 'Mary',
     2: 'likes',
     3: 'movies',
     4: 'to',
     5: 'too',
     6: 'watch',
     7: 'also',
     8: 'football',
     9: 'games',
     10: 'hates'}

```python
# Das resultierende BOW-Modell ist zudem sparse als Liste von Tupeln gespeichert,
# d.h. Dimensionen mit 0 vorkommenden Termen werden ausgelassen (aus Performanzgründen)
# Das erste Element des Tuples korrespondiert mit dem Termindex im Dictionary, wohingegen
# das zweite Element die absolute Termfrequenz darstellt
bow_corpus = [dictionary.doc2bow(doc) for doc in corpus]
bow_corpus
```

    [[(0, 1), (1, 1), (2, 2), (3, 2), (4, 1), (5, 1), (6, 1)],
     [(0, 1), (1, 1), (2, 1), (4, 1), (6, 1), (7, 1), (8, 2), (9, 1), (10, 1)]]

```python
# neue Terme können mit dem BOW-Modell nun in den gleichen Vektorraum transformiert werden
# unbekannte Terme werden dabei ausgelassen
vec_known = dictionary.doc2bow("Mary likes John".split())
vec_partially_unknown = dictionary.doc2bow("Mary likes soccer".split())
vec_unknown = dictionary.doc2bow("unknown".split())

print(f"BOW-Vektor (alle Terme in Eingabe bekannt): {vec_known})")
print(f"BOW-Vektor (teilweise unbekannte Terme in Eingabe): {vec_partially_unknown})")
print(f"BOW-Vektor (nur unbekannte Terme in Eingabe: {vec_unknown})")
```

    BOW-Vektor (alle Terme in Eingabe bekannt): [(0, 1), (1, 1), (2, 1)])
    BOW-Vektor (teilweise unbekannte Terme in Eingabe): [(1, 1), (2, 1)])
    BOW-Vektor (nur unbekannte Terme in Eingabe: [])

```python
# Zur Visualisierung dieser hochdimensionalen Vektoren können diese
# über verschiedene Dimensionalitätsreduktionsverfahren (z.B. PCA / t-SNE)
# auf 2- oder 3-D reduziert werden
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np

d1_bow = [1, 2, 1, 1, 2, 1, 1, 0, 0, 0, 0]
d2_bow = [1, 1, 1, 1, 0, 1, 0, 1, 2, 1, 1]

arr1 = np.array(d1_bow).reshape(1,-1)
arr2 = np.array(d2_bow).reshape(1,-1)

X_bow = np.concatenate((arr1,arr2),axis=0)

print(f"Term-Dokument-Matrix (chronologisch):\n{X_bow}")
print()

# Reduzierung der Daten auf 2-D
tsne = TSNE(n_components=2, random_state=42)
X_bow_2d = tsne.fit_transform(X_bow)

print(f"Term-Dokument-Matrix (2-D):\n{X_bow_2d}")

# Plotten der Ergebnisse
soa = np.array([[0, 0, X_bow_2d[0][0], X_bow_2d[0][1]], [0, 0, X_bow_2d[1][0], X_bow_2d[1][1]]])
X, Y, U, V = zip(*soa)
plt.figure()
ax = plt.gca()
ax.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1, color = ["r","b"])
for i,p in enumerate(X_bow_2d):
    ax.text(p[0], p[1], f"d{i+1}")

ax.set_xlim([-2000, 2000])
ax.set_ylim([-2000, 2000])
plt.draw()
plt.show()
```

### 3.2. Term-Term-Matrix / Wort-Wort-Matrix

Eine Term-Term-Matrix greift den Grundgedanken hinter den BOW-Modellen zur Semantik-Modellierung auf, indem nicht ein gesamtes Dokument als "Bedeutungskontext" festgelegt wird, sondern in dem Trainingskorpus nur gezählt wird, wie oft welche Wörter in einem bestimmten festgelegten "Fensterkontext" vorkommen. Zur Bestimmung dieser Frequenzen wird jeweils ein relativ kleines Fenster (z.B. +/-2 Wörter) durch das Trainingskorpus "geschoben", um den direkt umgebenden Kontext zu bestimmen. Wir werden diesen Ansatz bei word2vec wiedersehen, indem darüber Trainingsbeispiele für den zugrundeliegenden Klassifikator aus dem Input-Text generiert werden.

*Abb. 2: Illustration des fenster-basierten Ansatzes zur Erzeugung einer Term-Term-Matrix
(Quelle: http://mccormickml.com/assets/word2vec/training_data.png)*

Für die Dimensionen der Term-Term-Matrix gilt:

- Anzahl Spalten = |V| (V=Vokabular)
- Anzahl Zeilen = |V| (V=Vokabular)

## 4. Gewichtung von frequenzbasierten Vektoren
**Lernziele**

- Was ist TF-IDF? Warum sind reine Termfrequenzen unzureichend zur Semantikdifferenzierung?
- Was sind Anwendungsbereiche dieses Gewichtungsmaßes?

## 4.1. TF-IDF
Obgleich die Frequenz eines Wortes in einem Text sicherlich auch z.T. semantisch motiviert sein kann, lassen sich darüber keine Rückschlüsse schließen, ob diese Wort auch bedeutsam für die Bedeutungsdifferenzierung zwischen verschiedenen Texten ist. So sind z.B. Funktionswörter zwar hochfrequent, allerdings nicht nützlich für die Erfassung einer spezifischen Textsemantik, Hapax Legomena hingegen wären es schon. Diese hätten allerdings wieder den Nachteil, dass sie zu spezifisch für einen Einzeltext sind, um über mehrere Texte hinweg eine gemeinsame Semantik erfassen zu können. Jurafsky und Martin (2019) kommentieren dies treffend mit

>It’s a bit of a paradox. Words that occur nearby frequently (maybe pie nearby
cherry) are more important than words that only appear once or twice. Yet words
that are too frequent—ubiquitous, like the or good— are unimportant. How can we
balance these two conflicting constraints?

Zur Gewichtung dieser sehr unterschiedlichen Anforderungen wurde in der IR-Forschung der **TF-IDF-Algorithmus** entwickelt. Ziel des Algorithmus ist eine Gewichtung der reinen Frequenzwerte.

Die zwei Komponenten zur Berechnung dieser TF-IDF-Maße sind

**TF** = Termfrequenz

**IDF** = Inverse Dokumentfrequenz

#### Bestimmung von TF
\begin{align}
t \widehat{=}"Term"\\
d \widehat{=} "Dokument"\\
\end{align}

- Bestimmung der Vorkommenshäufigkeit eines Terms t in Dokument d
\begin{align}
tf_{t,d} = count(t,d)
\end{align}

- Alternativ: Log-Skalierung der reinen Termfrequenz
\begin{align}
tf_{t,d} = log_{10}(count(t,d)+1)
\end{align}

#### Bestimmung von IDF
\begin{align}
df_{t} \widehat{=}"Dokumentfrequenz\ eines\ Terms\ t"\\
N \widehat{=} "Anzahl\ der\ Dokumente"\\
\end{align}

- Bestimmung der inversen Dokumentfrequenz
\begin{align}
idf_{t} = log_{10}\left (\frac{N}{df_{t}}\right )
\end{align}

#### Bestimmung von TF-IDF
\begin{align}
tfidf_{t,d} = tf_{t,d} \cdot idf_{t}
\end{align}

Durch die Multiplikation der beiden Maße ergibt sich eine gleichmäßige Gewichtung der Frequenzinformation pro Dokument sowie über alle Dokumente hinweg.

**Hinweis:**

Neben den hier vorgestellten Gewichtungen existieren eine Reihe von alternativen Formeln zur Bestimmung der einzelnen Formelbestandteile. Formal werden diese häufig in der sog. [SMART-Notation](https://en.wikipedia.org/wiki/SMART_Information_Retrieval_System) angegeben. Diese wird z.B. auch in Gensim sowie in anderen IR-Systemen verwendet.

```python
# TFIDF-Transformationen
from gensim.models import TfidfModel

tfidf=TfidfModel(bow_corpus)
pprint(list(tfidf[bow_corpus]))
```

    2019-12-12 16:29:55,112 : INFO : collecting document frequencies
    2019-12-12 16:29:55,113 : INFO : PROGRESS: processing document #0
    2019-12-12 16:29:55,115 : INFO : calculating IDF weights for 2 documents and 11 features (16 matrix non-zeros)

    [[(3, 0.8944271909999159), (5, 0.4472135954999579)],
     [(7, 0.3779644730092272),
      (8, 0.7559289460184544),
      (9, 0.3779644730092272),
      (10, 0.3779644730092272)]]

```python
# Erinnerung: So sah unser Spielzeugkorpus aus
pprint({v:k for k,v in dictionary.token2id.items()})
print()
pprint(corpus, width=100)
```

    {0: 'John',
     1: 'Mary',
     2: 'likes',
     3: 'movies',
     4: 'to',
     5: 'too',
     6: 'watch',
     7: 'also',
     8: 'football',
     9: 'games',
     10: 'hates'}

    [['John', 'likes', 'to', 'watch', 'movies', 'Mary', 'likes', 'movies', 'too'],
     ['John', 'also', 'likes', 'to', 'watch', 'football', 'games', 'Mary', 'hates', 'football']]

### 4.2. Pointwise Mutual Information
Eine alternative Gewichtungsfunktion für Term-Term- oder Term-Dokument-Matrizen stellt die **Pointwise Mutual Information (PMI)** dar. Grundsätzlich wird hierbei ermittelt, wie oft zwei Ereignisse gemeinsam auftreten in Relation zu ihrem Auftreten, wenn sie unabhängig voneinander wären. Durch Verhältnis dieser beiden Wahrscheinlichkeiten zueinander kann dann bestimmt werden, wie oft zwei Wörter mehr oder weniger häufig auftreten als wir es zufällig erwarten würden.

\begin{align}
w \widehat{=}"Zielwort"\\
c \widehat{=} "Kontextwort"\\
PMI(w,c) = log_{2} \frac{P(w,c)}{P(w)P(c)}
\end{align}

Üblicherweise wird jedoch nur die Positive Pointwise Mutual Information (PPMI) verwendet, da negative PMI-Werte zum einen problematisch hinsichtlich ihrer Interpretierbarkeit sind und zum anderen unzuverlässig bei geringeren Korpusgrößen sind. In der Regeln werden alle negativen PMI-Werte deswegen durch 0 ersetzt.

\begin{align}
PPMI(w,c) = max(log_{2} \frac{P(w,c)}{P(w)P(c)},0)
\end{align}

Da die PPMI für infrequente Ereignisse generell höher ist, wird häufig die Wahrscheinlichkeit des Kontextwortes durch die Anwendung einer "Erhöhungsfunktion" gesteigert. Durch ein Setzen von Alpha = 0.75 wird die Warscheinlichkeit für infrequente Ereignisse etwas gesteigert, indem hochfrequente etwas weniger Wahrscheinlichkeitsmasse erhalten.

\begin{align}
P_{\alpha}(c) = \frac{ count(c)^\alpha}{\sum_c count(c)^\alpha}\\
PPMI(w,c) = max(log_{2} \frac{P(w,c)}{P(w)P_{\alpha}(c)},0)\\
\end{align}

Ein Beispiel für PPMI-Scores zwischen verschiedenen Ziel- und Kontextwörtern in einem Wikipedia-Dump, findet sich [hier](https://en.wikipedia.org/wiki/Pointwise_mutual_information).

### 4.3. Anwendungsfelder
TF-IDF-Modelle dienen häufig als **Base-Line zur Bestimmung einer einfachen Semantik** für Dokumente bzw. Sucheingaben (IR-Perspektive) innerhalb eines Vektorraums. Zur Bestimmung der Ähnlichkeit zwischen Vektoren in einem Vektorraum fehlt uns allerdings noch eine sogenannte Ähnlichkeitsmetrik, welche im Folgenden vorgestellt werden soll.

```python

```

```python

```

```python

```

```python

```

```python

```

## 5. Ähnlichkeitsbestimmung im Vektorraum
**Lernziele:**
- Was ist die Kosinusähnlichkeit von zwei Vektoren?

###  Kosinusähnlichkeit
Die Kosinusähnlichkeit zweier Vektoren *v* und *w* ist definiert als:

\begin{align}
cosine(v,w) = {\mathbf{v} \cdot \mathbf{w} \over |\mathbf{v}||\mathbf{w}|} = \frac{ \sum\limits_{i=1}^{N}{v_i  w_i} }{ \sqrt{\sum\limits_{i=1}^{N}{v_i^2}}  \sqrt{\sum\limits_{i=1}^{N}{w_i^2}} }
\end{align}

Hierbei werden die beiden Vektoren miteinander hinsichlich des Kosinus ihres eingeschlossenen Winkels verglichen. Das Maß dient also zur Interpretation der Lage von zwei Vektoren zueinander.

\begin{align}
cosine(v,w) = 1 \widehat{=} "Vektoren\ haben\ gleiche\ Ausrichtung" \widehat{=} "Ähnlichkeit"\\
cosine(v,w) = 0 \widehat{=} "Vektoren\ stehen\ orthogonal\ zueinander" \widehat{=} "keine\ Ähnlichkeit"\\
cosine(v,w) = -1 \widehat{=} "Vektoren\ haben\ entgegengesetzte\ Ausrichtung" \widehat{=} "Unähnlichkeit"\\
\end{align}

Graphisch kann dieses Verhältnis folgendermaßen visualisiert werden:

*Abb. 3: Visualisierung der Kosinus-Ähnlichkeit zwischen Vektoren
Quelle: http://blog.christianperone.com/2013/09/machine-learning-cosine-similarity-for-vector-space-models-part-iii/*

Über die Ähnlichkeit von Vektoren zueinander kann anschließend z.B. bestimmt werden, welche Sucheingabe am ähnlichsten zu welchem Dokument ist.

```python
# Suche nach dem ähnlichsten Dokument gegeben einen Suchanfrage/Query
# Hinweis: TF-IDF (transformiertes Korpus als Grundlage)
from gensim.similarities import MatrixSimilarity

index = MatrixSimilarity(tfidf[bow_corpus])

query_document1 = 'movies'.split()
query_document2 = 'football'.split()
query_document3 = 'movies football'.split()

query_bow1 = dictionary.doc2bow(query_document1)
query_bow2 = dictionary.doc2bow(query_document2)
query_bow3 = dictionary.doc2bow(query_document3)

sims1 = index[tfidf[query_bow1]]
sims2 = index[tfidf[query_bow2]]
sims3 = index[tfidf[query_bow3]]

print(f"Query: {query_document1}\nKosinus-Ähnlichkeit: {list(enumerate(sims1))}", end="\n\n")
print(f"Query: {query_document2}\nKosinus-Ähnlichkeit: {list(enumerate(sims2))}", end="\n\n")
print(f"Query: {query_document3}\nKosinus-Ähnlichkeit: {list(enumerate(sims3))}", end="\n\n")
```

    2019-12-12 16:29:55,173 : WARNING : scanning corpus to determine the number of features (consider setting `num_features` explicitly)
    2019-12-12 16:29:55,175 : INFO : creating matrix with 2 documents and 11 features

    Query: ['movies']
    Kosinus-Ähnlichkeit: [(0, 0.8944272), (1, 0.0)]

    Query: ['football']
    Kosinus-Ähnlichkeit: [(0, 0.0), (1, 0.75592893)]

    Query: ['movies', 'football']
    Kosinus-Ähnlichkeit: [(0, 0.6324555), (1, 0.5345225)]

```python

```

```python

```

## Semantik über Dense Vectors
**Lernziele:**
- Was sind Dense Vectors?
- Welche Algorithmen / Verfahren existieren zur Generierung von Dense Vectors?
- Was sind Contextualized String Embeddings?

### 6.1. word2vec
Neben der in [3.](##3.-Semantik-über-Sparse-Vectors) und [4.](##4.-Gewichtung-von-frequenzbasierten-Vektoren) dargestellten Codierung von Sprachdaten in einem dünnbesetzten Vektorraum existiert eine alternative Codierung in einem dichtbesetzten Vektorraum.

Dies hat u.a. folgende Vorteile:
- bessere Erfassung von Synonymie
- weniger Features für das Training
- speichereffizienter

Als **eine** Methode für die Erzeugung von deartigen Vektoren zur Repräsentation der Wortsemantik wird das Softwarepaket **word2vec** (Mikolov et al., 2013a/b) vorgestellt, welches folgende Algorithmen umfasst:
- **Skip-Gram**
- Continuous Bag of Words (CBOW)

*Abb. 4: Vergleich Skip-Gram vs. CBOW-Architektur
Quelle: Mikolov et al., 2013a*

Aus Gründen der Einfachheit wird nicht die Variante des Skip-Gram-Algorithmus mit mehreren Klassen (Softmax Layer) vorgestellt, sondern das **Skip-Gram mit negativem Sampling**, welches das Training dieser Embeddings auf ein binäres Klassifikationsproblem runterbricht.

**Methode**
>1. Treat the target word and a neighboring context word as positive examples.
>2. Randomly sample other words in the lexicon to get negative samples.
>3. Use logistic regression to train a classifier to distinguish those two cases.
>4. Use the regression weights as the embeddings.
(Jurafsky & Martin, 2019)

*Abb. 5: Extraktion von positiven Trainingsbeispielen aus den Trainingsdaten*

*Abb. 6: Generierung von negativen Trainingsbeispielen aus den Trainingsdaten*

Zur Bestimmung des geeigneten Störworts (*noise word*) für die negativen Trainingsbeispiele, wird eine gewichtete Unigramm-Frequenz mit dem Faktor Alpha zugrunde gelegt:

\begin{align}
P_{\alpha}(w) = \frac{count(w)^\alpha}{\sum_{w'} count(w)^\alpha}
\end{align}

Die Gewichte werden hierbei für das Training zunächst auf zufällige Werte initialisiert. Durch den Trainingsprozess werden diese Gewichte schrittweise für die einzelnen Trainingsbeispiel so angepasst, dass der Klassifikator immer genauer unterscheiden kann, ob ein Kontextwort neben einem Zielwort auftaucht. Diese Gewichte können nach dem Training als Embedding genutzt werden, um hochdimensionale Wortvektoren in einen niedriger dimensionierten Raum zu projizieren.

*Abb. 7: Nutzung der trainierten Gewichte des Klassifikators zur Einbettung von hochdimensionalen Wortvektoren
Quelle: http://mccormickml.com/assets/word2vec/word2vec_weight_matrix_lookup_table.png*

Für das Training des Klassifikators müssen folgende zwei Optimierungsziele beachtet werden:

>1. **Maximierung** der Ähnlichkeit für das Zielwort- und Kontextwort-Paare (t,c) gezogen aus der Menge der positiven Trainingsbeispiele
>2. **Minimierung** der Ähnlichkeit von (t,c)-Paaren gezogenen aus der Menge der negativen Trainingsbeispiele

Formal definiert ergibt sich daher für das Lernziel L(Theta) über die **gesamten Trainingsdaten** hinweg:

\begin{align}
L(\theta) = \sum _{(t,c)\in +}logP(+|t,c) + \sum _{(t,c)\in -}logP(-|t,c)
\end{align}

Für jedes **einzelne Zielwort-Kontextwort-Paar** (t,c) mit Noise-Wörtern n1..ni ergibt sich somit folgendes Lernziel:
\begin{align}
L(\theta) = logP(+|t,c) + \sum ^k_{i=1}logP(-|t,n_{1})\\
\end{align}

Nach erfolgreichem Training liegen zwei separate Embeddings/Gewichtsmatrizen für jedes Wort *w* vor
1. die Zielembedding *T*
2. das Kontextembedding *C*

*Abb. 8: Visualisierung des Ziel- und Kontextembeddings mit ihren jeweiligen Trainingszielen
(entnommen aus Jurafsky & Martin, 2019)*

**Hinweis:**
Mit dem vorgestellten word2vec-Verfahren können zwar bekannte Wörter in einem Korpus relativ schnell in eine entsprechende dichte Repräsentation transformiert werden, allerdings können unbekannte Wörter nicht in diesen Vektorraum überführt werden.

```python
# Laden des vortrainierten word2vec-Google-News-Korpus
# Quelle:
# https://radimrehurek.com/gensim/auto_examples/tutorials/run_word2vec.html#sphx-glr-auto-examples-tutorials-run-word2vec-py
import gensim.downloader as api
word2vec = api.load('word2vec-google-news-300')

```

    2019-12-12 16:29:56,462 : INFO : loading projection weights from C:\Users\mvogel/gensim-data\word2vec-google-news-300\word2vec-google-news-300.gz
    2019-12-12 16:34:18,919 : INFO : loaded (3000000, 300) matrix from C:\Users\mvogel/gensim-data\word2vec-google-news-300\word2vec-google-news-300.gz

```python
from sklearn.metrics.pairwise import cosine_similarity

# Test: kompositionelle Semantik bzw. Analogieschlüsse über Wortvektoren möglich?
queen_estimate = word2vec.get_vector("king") - word2vec.get_vector("man") + word2vec.get_vector("woman")
queen = word2vec.get_vector("queen")

sim_queen_queen_estimate = cosine_similarity(queen.reshape(1, -1), queen_estimate.reshape(1, -1))[0][0]

print(f"Kosinus-Ähnlichkeit zwischen kompositionellem Vektor zu 'queen' und tatsächlichem Wortvektor 'queen':\n{sim_queen_queen_estimate}", end="\n\n")

rome_estimate = word2vec.get_vector("Paris") - word2vec.get_vector("France") + word2vec.get_vector("Italy")
rome = word2vec.get_vector("Rome")

sim_rome_rome_estimate = cosine_similarity(rome.reshape(1, -1), rome_estimate.reshape(1, -1))[0][0]

print(f"Kosinus-Ähnlichkeit zwischen kompositionellem Vektor zu 'Rome' und tatsächlichem Wortvektor 'Rome':\n{sim_rome_rome_estimate}")

```

    Kosinus-Ähnlichkeit zwischen kompositionellem Vektor zu 'queen' und tatsächlichem Wortvektor 'queen':
    0.7300517559051514

    Kosinus-Ähnlichkeit zwischen kompositionellem Vektor zu 'Rome' und tatsächlichem Wortvektor 'Rome':
    0.7073157429695129

```python
# Wie ähnlich sind sich zwei Wörter?
w1 = "peasant"
w2 = "king"

sim_w1_w2 = cosine_similarity(word2vec.get_vector(w1).reshape(1, -1).reshape(1, -1), word2vec.get_vector(w2).reshape(1, -1))[0][0]
print(f"Kosinus-Ähnlichkeit zwischen '{w1}' und '{w2}':\n{sim_w1_w2}")

```

    Kosinus-Ähnlichkeit zwischen 'peasant' und 'king':
    0.18507906794548035

**Hinweis:**

Die gesamten word2vec-Embeddings von Google News können **interaktiv** auf folgenden Seiten durchsucht werden:
- https://projector.tensorflow.org/
   - 2/3-D Darstellung der Daten möglich, Suche nach den n ähnlichsten Wörtern
- https://rare-technologies.com/word2vec-tutorial/#app
   - Web-Demo für Ähnlichkeitssuchen (mit "Analogieschlüssen", Filtern von untypischen Wörtern aus einer Reihe etc.)

### 6.2. weitere Verfahren

Folgendes [Github-Repo](https://github.com/Hironsan/awesome-embedding-models) listet eine Reihe von Algorithmen zur Generierung von Embeddings aus unannotierten Textdaten.

Aktuell (Stand: 12/2019) geläufige Verfahren sind hierbei u.a.:
- FastText
- Glove
- ELMo
- BERT

Im Detail kann auf die einzelnen Embedding-Verfahren nicht eingegangen werden. Neue Verfahren gehen allerdings deutlich über rein wort-basierte semantische Repräsentationen hinaus, indem z.B. versucht wird, über neuronale Lernverfahren eine semantische Repräsentation von Sequenzen innerhalb eines Kontexts (bspw. Satz) zu erlernen. Hierbei wird die Information deutlich komplexer modelliert als z.B. bei word2vec. Zudem werden verschiedene Embeddings häufig miteinander kombiniert (*stacking*), um verschiedene Bedeutungsaspekte besser modellieren zu können.

Um einen ersten Eindruck für diese Arten von Embeddings zu vermitteln, stelle ich euch ein neues Paper (Akbik et al., 2018) zu "Contextualized String Embeddings" vor, welches für eine Reihe von NLP-Downstream-Tasks, bspw. **NER** bis vor knapp 1 Jahr State-of-The-Art-Resulate auf einer Reihe von öffentlich zugänglichen Evaluationskorpora erzielen konnte ([vgl. Link](https://paperswithcode.com/task/named-entity-recognition-ner))

### 6.3. Contextualized String Embeddings
Eine (Python-)Implementation dieser Embeddings, inklusive vortrainierter Sprachmodelle für diverse Sprachen, findet sich unter folgendem [Github-Repo](https://github.com/zalandoresearch/flair). Die von Akbik et al. (2018) vorgeschlagenen Embeddings vereinen hierbei verschiedene Ansätze, welche für die Erfassung der Wortsemantik erforscht wurden:
- 'klassische' Word Embeddings
- Features auf Character-Ebene
- Kontextualisierung von Word Embeddings

Grundsätzlich werden zunächst die Features auf Character-Ebene durch ein bilaterales Long-Short-Term-Memory-Netzwerk (BiLSTM) codiert ([s. hier für eine knappe Einführung zu LSTM](https://towardsdatascience.com/illustrated-guide-to-lstms-and-gru-s-a-step-by-step-explanation-44e9eb85bf21)). Grob gesagt ermöglichen diese Art von Netzwerk die Speicherung von Information über eine beliebige Sequenzlänge hinweg über sogenannten **Hidden States** und **Cell States**, ohne dass z.B. Information, die zu Beginn einer Sequenz erlernt wurde, später vergessen wird, ein Problem, dass bei 'klassischen' Rekurrenten Neuronalen Netzen auftritt. Der Informationsfluss wird dahingehend kontrolliert, dass über bestimmte **Gates** kontrolliert wird, welche Information in den Hidden Sates gespeichert wird oder nicht.

*Abb. 9: Schematische Darstellung der Funktionsweise einer LSTM- und GRU-Zelle*
- *Quelle:*
  - https://miro.medium.com/max/1516/1*yBXV9o5q7L_CvY7quJt3WQ.png

Das trainierte Character-Sprachmodell wird hierbei auf **Satzebene** antrainiert, um mit diesem Modell für jedes Wort in einer Sequenz ein kontextualisiertes Embedding extrahieren zu können.

*Abb. 10: Extraktion eines Contextualized String Embeddings für das Wort "Washington" auf Basis de
(Quelle: Akbik et al., 2018)*

Diese Embeddings können anschließend als Basis für einen Down-Stream-NLP-Taks dienen, in diesem Falle **NER** (letzter Layer hier: BiLSTM mit Conditonal Random Field (CRF)).

*Abb. 11: Übersicht der Netzwerkarchitektur für Contextualized String Embeddings für den Down-Stream-Task NER
(Quelle: Akbik et al., 2018)*

Die Autoren evaluieren ihre vorgeschlagene Netztwerkarchitektur im Detail sowohl quantitativ...

*Abb. 12: Evaluationsergebnisse für Contextualized String Embedddings in verschiedenen Netzwerkkonfigurationen für Sequenzlabeling (letzter Layer: BiLSTM-CRF)
(Quelle: Akbik et al., 2018)*

...als auch qualitativ

*Abb. 13: qualitative Evaluation der Semantik des Worts "Washington" in verschiedenen Satzkontexten
(Quelle: Akbik et al., 2018)*

**Ergebnisse**

*Hinweis:* Die aktuell besten Trainingskonfigurationen zur Reproduzierbarkeit der Ergebnisse aus dem Paper sind [hier](https://github.com/zalandoresearch/flair/blob/master/resources/docs/EXPERIMENTS.md) dokumentiert.
- **quantitativ**
  - für alle getesteten Trainingskonfigurationen haben Contextualized String Embeddings einen positiven Einfluss auf die Perfomanz bei Down-Stream-NLP-Tasks
  - neue State-of-the-Art-Resultate bei NER
  - gute Performanz bei syntaktischen Aufgaben (POS-Tagging)
  - Stacking von 'klassischen' Word Embeddings und Contextualized String Embeddings nützlich für Sequence Labeling
  - Task-spezifische Character Features sind nicht nötig

- **qualitativ**
  - Modell ist in der Lage, polyseme Ausdrücke, wie z.B. "Washington", korrekt über den umliegen Kontext zu disambiguieren

**Diskussion**
- Contextualized String Embeddings sind ein sinnvolles Instrument zum Erlernen von semantischen Repräsentationen auf unannotierten Daten
- Trainingszeit deutlich geringer als bei wort-basierten Modellen, da #Character < #Wörter
- Character-basierte Sprachmodelle sind unabhängig von einer Tokenisierung oder einem fixen Vokabular

```python
from flair.models import SequenceTagger
from flair.data import Sentence

# Mini-Demo: NER-Annotation mit vortrainiertem Flair-Modell
# Quellen:
# https://github.com/zalandoresearch/flair/blob/master/resources/docs/TUTORIAL_2_TAGGING.md
# https://github.com/zalandoresearch/flair/blob/master/resources/docs/TUTORIAL_3_WORD_EMBEDDING.md
tagger = SequenceTagger.load('ner')

sentence = Sentence('George Washington went to Washington .')

tagger.predict(sentence)

print(f"Annotierter Satz: {sentence.to_tagged_string()}")
```

    2019-12-12 16:34:28,206 : INFO : PyTorch version 1.3.1+cpu available.

    2019-12-12 16:34:29,034 loading file C:\Users\mvogel\.flair\models\en-ner-conll03-v0.4.pt
    Annotierter Satz: George  Washington <E-PER> went to Washington <S-LOC> .

```python
# Stacked Embeddings für diese Netz
stacked_embeddings = tagger.embeddings
stacked_embeddings
```

    StackedEmbeddings(
      (list_embedding_0): WordEmbeddings('/home/aakbik/.flair/embeddings/glove.gensim')
      (list_embedding_1): FlairEmbeddings(
        (lm): LanguageModel(
          (drop): Dropout(p=0.05, inplace=False)
          (encoder): Embedding(300, 100)
          (rnn): LSTM(100, 2048)
          (decoder): Linear(in_features=2048, out_features=300, bias=True)
        )
      )
      (list_embedding_2): FlairEmbeddings(
        (lm): LanguageModel(
          (drop): Dropout(p=0.05, inplace=False)
          (encoder): Embedding(300, 100)
          (rnn): LSTM(100, 2048)
          (decoder): Linear(in_features=2048, out_features=300, bias=True)
        )
      )
    )

```python
sentence = Sentence('The grass is green .')

# Embedding eines Satzes
stacked_embeddings.embed(sentence)

for token in sentence:
    print(token)
    print(token.embedding)
    print(token.embedding.shape)
    print()
```

    Token: 1 The
    tensor([-0.0382, -0.2449,  0.7281,  ...,  0.0080,  0.0566,  0.0259])
    torch.Size([4196])

    Token: 2 grass
    tensor([-0.8135,  0.9404, -0.2405,  ..., -0.0364, -0.0064, -0.3664])
    torch.Size([4196])

    Token: 3 is
    tensor([-0.5426,  0.4148,  1.0322,  ..., -0.0837, -0.1291,  0.0049])
    torch.Size([4196])

    Token: 4 green
    tensor([-0.6791,  0.3491, -0.2398,  ...,  0.0098, -0.0034,  0.0115])
    torch.Size([4196])

    Token: 5 .
    tensor([-0.3398,  0.2094,  0.4635,  ..., -0.0573, -0.0117, -0.0455])
    torch.Size([4196])

## 7. Diskussion
**Themen**
- Evaluation von Word Embeddings?
  - häufig mit externem Task verknüpft, z.B. Text Classification, POS-Tagging, NER etc.
    - Wie viel perfomanter wird ein System im Vergleich zu einem System ohne Embeddings/mit anderen semantischen Repäsentationsformen?
  - daneben aber auch intrinsische Evaluation (auf psycholinguisichen Korpora)
     - Wie ähnlich sind sich Word Embeddings und z.B. Merkmalsvektoren?
- Bias bei Word Embeddings?
  - Bias in den Daten produziert auch biased Embeddings (?)
  - Versuch der Korrektur von biased Embeddings (s. Jurafsky & Martin, 2019)

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```

```python

```


# DEMO

The demo is intended to understand the idea of generating qiskit

## Prerequisites

Qiskit is posible installing in 64-bit systems:

    -Ubuntu 16.04 or later
    -macOS 10.12.6 or later
    -Windows 7 or later

You need to have Python3.5 or higher installed.

The following command install qiskit.

```python
!pip install qiskit
```

    Defaulting to user installation because normal site-packages is not writeable
    Requirement already satisfied: qiskit in /opt/conda/lib/python3.7/site-packages (0.20.0)
    Requirement already satisfied: qiskit-ibmq-provider==0.8.0 in /opt/conda/lib/python3.7/site-packages (from qiskit) (0.8.0)
    Requirement already satisfied: qiskit-aer==0.6.1 in /opt/conda/lib/python3.7/site-packages (from qiskit) (0.6.1)
    Requirement already satisfied: qiskit-ignis==0.4.0 in /opt/conda/lib/python3.7/site-packages (from qiskit) (0.4.0)
    Requirement already satisfied: qiskit-terra==0.15.1 in /opt/conda/lib/python3.7/site-packages (from qiskit) (0.15.1)
    Requirement already satisfied: qiskit-aqua==0.7.5 in /opt/conda/lib/python3.7/site-packages (from qiskit) (0.7.5)
    Requirement already satisfied: requests>=2.19 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (2.24.0)
    Requirement already satisfied: websockets>=8; python_version > "3.6" in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (8.1)
    Requirement already satisfied: nest-asyncio!=1.1.0,>=1.0.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (1.4.0)
    Requirement already satisfied: urllib3>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (1.25.10)
    Requirement already satisfied: numpy>=1.13 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (1.19.1)
    Requirement already satisfied: requests-ntlm>=1.1.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (1.1.0)
    Requirement already satisfied: python-dateutil>=2.8.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit) (2.8.1)
    Requirement already satisfied: scipy>=1.0; python_version > "3.5" in /opt/conda/lib/python3.7/site-packages (from qiskit-aer==0.6.1->qiskit) (1.5.2)
    Requirement already satisfied: pybind11>=2.4 in /opt/conda/lib/python3.7/site-packages (from qiskit-aer==0.6.1->qiskit) (2.5.0)
    Requirement already satisfied: cython>=0.27.1 in /opt/conda/lib/python3.7/site-packages (from qiskit-aer==0.6.1->qiskit) (0.29.21)
    Requirement already satisfied: scikit-learn>=0.17 in /opt/conda/lib/python3.7/site-packages (from qiskit-ignis==0.4.0->qiskit) (0.23.2)
    Requirement already satisfied: setuptools>=40.1.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ignis==0.4.0->qiskit) (49.6.0.post20200814)
    Requirement already satisfied: retworkx>=0.4.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (0.4.0)
    Requirement already satisfied: fastjsonschema>=2.10 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (2.14.5)
    Requirement already satisfied: ply>=3.10 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (3.11)
    Requirement already satisfied: sympy>=1.3 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (1.5.1)
    Requirement already satisfied: dill>=0.3 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (0.3.2)
    Requirement already satisfied: psutil>=5 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (5.7.2)
    Requirement already satisfied: networkx>=2.2; python_version > "3.5" in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (2.5)
    Requirement already satisfied: python-constraint>=1.4 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (1.4.0)
    Requirement already satisfied: jsonschema>=2.6 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit) (3.2.0)
    Requirement already satisfied: fastdtw in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (0.3.4)
    Requirement already satisfied: dlx in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (1.0.4)
    Requirement already satisfied: docplex in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (2.15.194)
    Requirement already satisfied: h5py in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (2.10.0)
    Requirement already satisfied: pandas in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (1.1.1)
    Requirement already satisfied: quandl in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (3.5.2)
    Requirement already satisfied: yfinance in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit) (0.1.54)
    Requirement already satisfied: idna<3,>=2.5 in /opt/conda/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.8.0->qiskit) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /opt/conda/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.8.0->qiskit) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.8.0->qiskit) (2020.6.20)
    Requirement already satisfied: cryptography>=1.3 in /opt/conda/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit) (3.1)
    Requirement already satisfied: ntlm-auth>=1.0.2 in /opt/conda/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit) (1.5.0)
    Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.7/site-packages (from python-dateutil>=2.8.0->qiskit-ibmq-provider==0.8.0->qiskit) (1.15.0)
    Requirement already satisfied: joblib>=0.11 in /opt/conda/lib/python3.7/site-packages (from scikit-learn>=0.17->qiskit-ignis==0.4.0->qiskit) (0.16.0)
    Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.7/site-packages (from scikit-learn>=0.17->qiskit-ignis==0.4.0->qiskit) (2.1.0)
    Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.7/site-packages (from sympy>=1.3->qiskit-terra==0.15.1->qiskit) (1.1.0)
    Requirement already satisfied: decorator>=4.3.0 in /opt/conda/lib/python3.7/site-packages (from networkx>=2.2; python_version > "3.5"->qiskit-terra==0.15.1->qiskit) (4.4.2)
    Requirement already satisfied: pyrsistent>=0.14.0 in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit) (0.16.0)
    Requirement already satisfied: importlib-metadata; python_version < "3.8" in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit) (1.7.0)
    Requirement already satisfied: attrs>=17.4.0 in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit) (20.1.0)
    Requirement already satisfied: pytz>=2017.2 in /opt/conda/lib/python3.7/site-packages (from pandas->qiskit-aqua==0.7.5->qiskit) (2020.1)
    Requirement already satisfied: inflection>=0.3.1 in /opt/conda/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.7.5->qiskit) (0.5.1)
    Requirement already satisfied: more-itertools in /opt/conda/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.7.5->qiskit) (8.5.0)
    Requirement already satisfied: multitasking>=0.0.7 in /opt/conda/lib/python3.7/site-packages (from yfinance->qiskit-aqua==0.7.5->qiskit) (0.0.9)
    Requirement already satisfied: cffi!=1.11.3,>=1.8 in /opt/conda/lib/python3.7/site-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit) (1.14.1)
    Requirement already satisfied: zipp>=0.5 in /opt/conda/lib/python3.7/site-packages (from importlib-metadata; python_version < "3.8"->jsonschema>=2.6->qiskit-terra==0.15.1->qiskit) (3.1.0)
    Requirement already satisfied: pycparser in /opt/conda/lib/python3.7/site-packages (from cffi!=1.11.3,>=1.8->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit) (2.20)
    ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/home/jovyan/.local/lib/python3.7'
    Check the permissions.

In case you need to install in jupyter Notebooks, you need to use the next command

```python
!pip install qiskit[visualization]
```

    Defaulting to user installation because normal site-packages is not writeable
    Requirement already satisfied: qiskit[visualization] in /opt/conda/lib/python3.7/site-packages (0.20.0)
    Requirement already satisfied: qiskit-terra==0.15.1 in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.15.1)
    Requirement already satisfied: qiskit-ignis==0.4.0 in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.4.0)
    Requirement already satisfied: qiskit-aer==0.6.1 in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.6.1)
    Requirement already satisfied: qiskit-aqua==0.7.5 in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.7.5)
    Requirement already satisfied: qiskit-ibmq-provider==0.8.0 in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.8.0)
    Requirement already satisfied: matplotlib>=2.1; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (3.3.1)
    Requirement already satisfied: pylatexenc>=1.4; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (2.7)
    Requirement already satisfied: pygments>=2.4; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (2.6.1)
    Requirement already satisfied: seaborn>=0.9.0; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (0.10.1)
    Requirement already satisfied: ipywidgets>=7.3.0; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (7.5.1)
    Requirement already satisfied: pillow>=4.2.1; extra == "visualization" in /opt/conda/lib/python3.7/site-packages (from qiskit[visualization]) (7.2.0)
    Collecting pydot; extra == "visualization"
      Using cached pydot-1.4.1-py2.py3-none-any.whl (19 kB)
    Requirement already satisfied: python-constraint>=1.4 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (1.4.0)
    Requirement already satisfied: scipy>=1.4 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (1.5.2)
    Requirement already satisfied: fastjsonschema>=2.10 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (2.14.5)
    Requirement already satisfied: networkx>=2.2; python_version > "3.5" in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (2.5)
    Requirement already satisfied: retworkx>=0.4.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (0.4.0)
    Requirement already satisfied: sympy>=1.3 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (1.5.1)
    Requirement already satisfied: numpy>=1.17 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (1.19.1)
    Requirement already satisfied: ply>=3.10 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (3.11)
    Requirement already satisfied: python-dateutil>=2.8.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (2.8.1)
    Requirement already satisfied: psutil>=5 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (5.7.2)
    Requirement already satisfied: jsonschema>=2.6 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (3.2.0)
    Requirement already satisfied: dill>=0.3 in /opt/conda/lib/python3.7/site-packages (from qiskit-terra==0.15.1->qiskit[visualization]) (0.3.2)
    Requirement already satisfied: scikit-learn>=0.17 in /opt/conda/lib/python3.7/site-packages (from qiskit-ignis==0.4.0->qiskit[visualization]) (0.23.2)
    Requirement already satisfied: setuptools>=40.1.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ignis==0.4.0->qiskit[visualization]) (49.6.0.post20200814)
    Requirement already satisfied: pybind11>=2.4 in /opt/conda/lib/python3.7/site-packages (from qiskit-aer==0.6.1->qiskit[visualization]) (2.5.0)
    Requirement already satisfied: cython>=0.27.1 in /opt/conda/lib/python3.7/site-packages (from qiskit-aer==0.6.1->qiskit[visualization]) (0.29.21)
    Requirement already satisfied: docplex in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (2.15.194)
    Requirement already satisfied: fastdtw in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (0.3.4)
    Requirement already satisfied: dlx in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (1.0.4)
    Requirement already satisfied: pandas in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (1.1.1)
    Requirement already satisfied: h5py in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (2.10.0)
    Requirement already satisfied: quandl in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (3.5.2)
    Requirement already satisfied: yfinance in /opt/conda/lib/python3.7/site-packages (from qiskit-aqua==0.7.5->qiskit[visualization]) (0.1.54)
    Requirement already satisfied: requests-ntlm>=1.1.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (1.1.0)
    Requirement already satisfied: urllib3>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (1.25.10)
    Requirement already satisfied: requests>=2.19 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (2.24.0)
    Requirement already satisfied: nest-asyncio!=1.1.0,>=1.0.0 in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (1.4.0)
    Requirement already satisfied: websockets>=8; python_version > "3.6" in /opt/conda/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (8.1)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in /opt/conda/lib/python3.7/site-packages (from matplotlib>=2.1; extra == "visualization"->qiskit[visualization]) (2.4.7)
    Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.7/site-packages (from matplotlib>=2.1; extra == "visualization"->qiskit[visualization]) (0.10.0)
    Requirement already satisfied: certifi>=2020.06.20 in /opt/conda/lib/python3.7/site-packages (from matplotlib>=2.1; extra == "visualization"->qiskit[visualization]) (2020.6.20)
    Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.7/site-packages (from matplotlib>=2.1; extra == "visualization"->qiskit[visualization]) (1.2.0)
    Requirement already satisfied: ipython>=4.0.0; python_version >= "3.3" in /opt/conda/lib/python3.7/site-packages (from ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (7.18.1)
    Requirement already satisfied: ipykernel>=4.5.1 in /opt/conda/lib/python3.7/site-packages (from ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (5.3.4)
    Requirement already satisfied: widgetsnbextension~=3.5.0 in /opt/conda/lib/python3.7/site-packages (from ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (3.5.1)
    Requirement already satisfied: nbformat>=4.2.0 in /opt/conda/lib/python3.7/site-packages (from ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (5.0.7)
    Requirement already satisfied: traitlets>=4.3.1 in /opt/conda/lib/python3.7/site-packages (from ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (4.3.3)
    Requirement already satisfied: decorator>=4.3.0 in /opt/conda/lib/python3.7/site-packages (from networkx>=2.2; python_version > "3.5"->qiskit-terra==0.15.1->qiskit[visualization]) (4.4.2)
    Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.7/site-packages (from sympy>=1.3->qiskit-terra==0.15.1->qiskit[visualization]) (1.1.0)
    Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.7/site-packages (from python-dateutil>=2.8.0->qiskit-terra==0.15.1->qiskit[visualization]) (1.15.0)
    Requirement already satisfied: importlib-metadata; python_version < "3.8" in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit[visualization]) (1.7.0)
    Requirement already satisfied: pyrsistent>=0.14.0 in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit[visualization]) (0.16.0)
    Requirement already satisfied: attrs>=17.4.0 in /opt/conda/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-terra==0.15.1->qiskit[visualization]) (20.1.0)
    Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.7/site-packages (from scikit-learn>=0.17->qiskit-ignis==0.4.0->qiskit[visualization]) (2.1.0)
    Requirement already satisfied: joblib>=0.11 in /opt/conda/lib/python3.7/site-packages (from scikit-learn>=0.17->qiskit-ignis==0.4.0->qiskit[visualization]) (0.16.0)
    Requirement already satisfied: pytz>=2017.2 in /opt/conda/lib/python3.7/site-packages (from pandas->qiskit-aqua==0.7.5->qiskit[visualization]) (2020.1)
    Requirement already satisfied: more-itertools in /opt/conda/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.7.5->qiskit[visualization]) (8.5.0)
    Requirement already satisfied: inflection>=0.3.1 in /opt/conda/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.7.5->qiskit[visualization]) (0.5.1)
    Requirement already satisfied: multitasking>=0.0.7 in /opt/conda/lib/python3.7/site-packages (from yfinance->qiskit-aqua==0.7.5->qiskit[visualization]) (0.0.9)
    Requirement already satisfied: ntlm-auth>=1.0.2 in /opt/conda/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (1.5.0)
    Requirement already satisfied: cryptography>=1.3 in /opt/conda/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (3.1)
    Requirement already satisfied: idna<3,>=2.5 in /opt/conda/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /opt/conda/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (3.0.4)
    Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /opt/conda/lib/python3.7/site-packages (from ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (3.0.7)
    Requirement already satisfied: backcall in /opt/conda/lib/python3.7/site-packages (from ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.2.0)
    Requirement already satisfied: pexpect>4.3; sys_platform != "win32" in /opt/conda/lib/python3.7/site-packages (from ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (4.8.0)
    Requirement already satisfied: jedi>=0.10 in /opt/conda/lib/python3.7/site-packages (from ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.17.2)
    Requirement already satisfied: pickleshare in /opt/conda/lib/python3.7/site-packages (from ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.7.5)
    Requirement already satisfied: tornado>=4.2 in /opt/conda/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (6.0.4)
    Requirement already satisfied: jupyter-client in /opt/conda/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (6.1.7)
    Requirement already satisfied: notebook>=4.4.1 in /opt/conda/lib/python3.7/site-packages (from widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (6.1.3)
    Requirement already satisfied: jupyter-core in /opt/conda/lib/python3.7/site-packages (from nbformat>=4.2.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (4.6.3)
    Requirement already satisfied: ipython-genutils in /opt/conda/lib/python3.7/site-packages (from nbformat>=4.2.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.2.0)
    Requirement already satisfied: zipp>=0.5 in /opt/conda/lib/python3.7/site-packages (from importlib-metadata; python_version < "3.8"->jsonschema>=2.6->qiskit-terra==0.15.1->qiskit[visualization]) (3.1.0)
    Requirement already satisfied: cffi!=1.11.3,>=1.8 in /opt/conda/lib/python3.7/site-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (1.14.1)
    Requirement already satisfied: wcwidth in /opt/conda/lib/python3.7/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.2.5)
    Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.7/site-packages (from pexpect>4.3; sys_platform != "win32"->ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.6.0)
    Requirement already satisfied: parso<0.8.0,>=0.7.0 in /opt/conda/lib/python3.7/site-packages (from jedi>=0.10->ipython>=4.0.0; python_version >= "3.3"->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.7.0)
    Requirement already satisfied: pyzmq>=13 in /opt/conda/lib/python3.7/site-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (19.0.2)
    Requirement already satisfied: argon2-cffi in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (20.1.0)
    Requirement already satisfied: Send2Trash in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (1.5.0)
    Requirement already satisfied: terminado>=0.8.3 in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.8.3)
    Requirement already satisfied: prometheus-client in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.8.0)
    Requirement already satisfied: nbconvert in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (5.6.1)
    Requirement already satisfied: jinja2 in /opt/conda/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (2.11.2)
    Requirement already satisfied: pycparser in /opt/conda/lib/python3.7/site-packages (from cffi!=1.11.3,>=1.8->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.8.0->qiskit[visualization]) (2.20)
    Requirement already satisfied: testpath in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.4.4)
    Requirement already satisfied: pandocfilters>=1.4.1 in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (1.4.2)
    Requirement already satisfied: mistune<2,>=0.8.1 in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.8.4)
    Requirement already satisfied: bleach in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (3.1.5)
    Requirement already satisfied: defusedxml in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.6.0)
    Requirement already satisfied: entrypoints>=0.2.2 in /opt/conda/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.3)
    Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/lib/python3.7/site-packages (from jinja2->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (1.1.1)
    Requirement already satisfied: packaging in /opt/conda/lib/python3.7/site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (20.4)
    Requirement already satisfied: webencodings in /opt/conda/lib/python3.7/site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=7.3.0; extra == "visualization"->qiskit[visualization]) (0.5.1)
    Installing collected packages: pydot
    ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/home/jovyan/.local/lib/python3.7'
    Check the permissions.

```python
!!pip install pylatexenc
```

    ['Defaulting to user installation because normal site-packages is not writeable',
     'Requirement already satisfied: pylatexenc in /opt/conda/lib/python3.7/site-packages (2.7)',
     "ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/home/jovyan/.local/lib/python3.7'",
     'Check the permissions.',
     '']

# Introduction
In order to validate that it has been installed correctly the module is called, if it does not send an error it means that you already have Qiskit installed

```python
import qiskit # call  the qiskit's module
```

Qiskit requires three components for the creation of quantum circuits:

    -Qubits or QuantumRegister (the inputs).
    -Classical bits or ClassicalRegister (number of qubits to be measured from the quantum circuit).
    -Quantum circuit or QuantumCircuit (the means to apply quantum gates to the qubits).

The most basic example is calling a qubit (which conveniently starts at the |0> state) and then measuring it, as shown below.

```python
qr = qiskit.QuantumRegister(1) # call a quantum bit (or qubit)
cr = qiskit.ClassicalRegister(1) # call a clasical bit
program = qiskit.QuantumCircuit(qr, cr) # The quantum circuit is generated from the previous qubit and bit
```

The measurement is made at the end of the quantum circuit indicating in list form the qubits that will correspond to the classic bits

```python
program.measure(qr,cr) # The qubit is measured, and the measurement result is stored in the classic bit.
```

    <qiskit.circuit.instructionset.InstructionSet at 0x7fb53a5f6690>

Qiskit has different ways to visualize the quantum circuit that was designed before.

```python
program.draw()
```

<pre style="word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: "Courier New",Courier,monospace">      ┌─┐
q0_0: ┤M├
      └╥┘
c0: 1/═╩═
       0 </pre>

```python
%matplotlib inline
program.draw(output="mpl")
```

The quantum circuit simulation process is performed by selecting a backend such as "qasm_simulator"

```python
job = qiskit.execute( program, qiskit.BasicAer.get_backend('qasm_simulator') )
```

To obtain the values of the measurement that was made on the quantum circuit is

```python
print( job.result().get_counts() )
```

    {'0': 1024}

When no modification is made to the qubit, its value will always be the default value which is 0. Another way to identify the output values is from a histogram that displays the total of measurements in the $|0>$ state

```python
qiskit.visualization.plot_histogram(job.result().get_counts(program))
```

Qiskit gives the opportunity to use quantum computers remotely. To access them, you need to have an account at https://quantum-computing.ibm.com, where by checking the section "my account" you will get a token to use a real quantum computer. If you save the account in your local computer, it will be remembered for future cloud executions of Qiskit code for a variety of real quantum computer backends.

```python
qiskit.IBMQ.save_account('my_token') #Replace the text my_token for your own token
```

    configrc.store_credentials:WARNING:2020-10-11 23:59:01,669: Credentials already present. Set overwrite=True to overwrite.

After having the key saved, use the load method and select a computer (the list can be found at the link https://quantum-computing.ibm.com/). For this example, the ibmq16_melbourne (has 15 qubits) is selected.

```python
qiskit.IBMQ.load_account() # load the token
provider = qiskit.IBMQ.get_provider('ibm-q') # select the provider
backend = provider.get_backend('ibmq_16_melbourne') # select the name of the quatum computer to use
print("real device:",backend.name())
job = qiskit.execute( program, backend )
```

    /opt/conda/lib/python3.7/site-packages/qiskit/providers/ibmq/ibmqfactory.py:192: UserWarning: Timestamps in IBMQ backend properties, jobs, and job results are all now in local time instead of UTC.
      warnings.warn('Timestamps in IBMQ backend properties, jobs, and job results '

    real device: ibmq_16_melbourne

```python
print( job.result().get_counts() ) # show the result
```

    {'0': 1016, '1': 8}

```python
qiskit.visualization.plot_histogram(job.result().get_counts())
```

# Single qubit

One way to define the qubit value is from the Statevector method

```python
from qiskit.quantum_info import Statevector
sv = Statevector.from_label('0') #indicates the state of a qubit
sv
```

    Statevector([1.+0.j, 0.+0.j],
                dims=(2,))

Show with the method data the state vector is equal to the |0> state

```python
sv.data
```

    array([1.+0.j, 0.+0.j])

A visual support is from the method of the qsphere that shows the state of the qubits (in this case, one qubit) on a sphere.

```python
from qiskit.visualization import plot_state_qsphere

plot_state_qsphere(sv.data)
```

## Apply X gate
The code below generates a quantum circuit that applies the X gate to the 0th qubit (as qubits in Qiskit are numbered 0, 1, 2,...), and shows the subsequent quantum state in the qsphere. The X gate is also called the NOT gate because in this basis of |0> and |1> (called the computational basis), the X gate flips the bit value of the qubit it is applied to.

```python
circuit = qiskit.QuantumCircuit(1)
circuit.x(0)
circuit.draw('mpl')
```

```python
sv_x = sv.evolve(circuit)
```

```python
plot_state_qsphere(sv_x.data)
```

## Apply Hadamard gate
The code below generates a quantum circuit that applies the Hadamard (H) gate to the 0th qubit, and shows the subsequent quantum state in the qsphere.

```python
circuit = qiskit.QuantumCircuit(1)
circuit.h(0)
circuit.draw('mpl')
```

```python
sv_h = sv.evolve(circuit)
```

```python
plot_state_qsphere(sv_h.data)
```

# Multiple Qubits

For the case of multiple qubits, we can perform quantum entanglement. Here, we entangle two qubits together by applying the Hadamard gate followed by the Controlled-X (CX) gate. The Controlled-X gate is also called the Controlled-NOT (CNOT) gate.

```python
sv_hcx = Statevector.from_label('00')
plot_state_qsphere(sv.data)
```

```python
circuit = qiskit.QuantumCircuit(2)
circuit.h(0)
circuit.cx(0,1)
circuit.draw('mpl')
```

```python
sv_entanglement = sv_hcx.evolve(circuit)
plot_state_qsphere(sv_entanglement.data)
```