aitech-moj-2023/wyk/01_Jezyk.org

9.9 KiB
Raw Blame History

Język — różne perspektywy

Słowo wstępne

W matematyce istnieją dwa spojrzenia na rzeczywistość: ciągłe i dyskretne.

Otaczająca nas rzeczywistość fizyczna jest z natury ciągła (przynajmniej jeśli nie operujemy w mikroskali), lecz język jest dyskretnym wyłomem w ciągłej rzeczywistości.

Lingwistyka matematyczna

Przypomnijmy sobie definicję języka przyjętą w lingwistyce matematycznej, w kontekście na przykład teorii automatów.

Alfabetem nazywamy skończony zbiór symboli.

Łańcuchem nad alfabetem $\Sigma$ nazywamy dowolny, skończony, ciąg złożony z symboli z $\Sigma$

Językiem nazywamy dowolny, skończony bądź nieskończony, zbiór łańcuchów.

W tym formalnym ujęciu językami są na przykład następujące zbiory:

  • $\{\mathit{poniedziałek},\mathit{wtorek},\mathit{środa},\mathit{czwartek},\mathit{piątek},\mathit{sobota},\mathit{niedziela}\}$
  • $\{\mathit{ab},\mathit{abb},\mathit{abbb},\mathit{abbbb},\ldots\}$

To podejście, z jednej strony oczywiście nie do końca się pokrywa się z potocznym rozumieniem słowa język, z drugiej kojarzy nam się z takimi narzędziami informatyki jak wyrażenia regularne, automaty skończenie stanowe czy gramatyki języków programowania.

  import regex as re
  rx = re.compile(r'ab+')

  rx.search('żabbba').group(0)

abbb

  import rstr

  rstr.xeger(r'ab+')

abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

Ujęcie probabilistyczne

Na tym wykładzie przyjmiemy inną perspektywą, częściowo ciągłą, opartą na probabilistyce. Język będziemy definiować poprzez rozkład prawdopodobieństwa: sensownym wypowiedziom czy tekstom będziemy przypisywać stosunkowe wysokie prawdopodobieństwo, „ułomnym” tekstom — niższe (być może zerowe).

Na ogół nie mamy jednak do czynienia z językiem jako takim tylko z jego przybliżeniami, modelami (model może być lepszy lub gorszy, ale przynajmniej powinien być użyteczny…). Formalnie $M$ nazywamy modelem języka (nad skończonym alfabetem $Sigma$), jeśli dyskretny określa rozkład prawdopodobieństwa $P_M$:

$$P_M \colon \Sigma^{*} \rightarrow [0,1].$$

Rzecz jasna, skoro mamy do czynienia z rozkładem prawdopodobieństwa, to:

$$\sum_{\alpha \in \Sigma^{*}} P_M(\alpha) = 1$$

Jeśli $M$ ma być modelem języka polskiego, oczekiwalibyśmy, że dla napisów:

  • $z_1$ — W tym stanie rzeczy pan Ignacy coraz częściej myślał o Wokulskim.
  • $z_2$ — Po wypełniony zbiornik pełny i należne kwotę, usłyszała w attendant
  • $z_3$ — xxxxyźźźźźit backspace hoooooooooop x y z

zachodzić będzie:

\[ P_M(z_1) > P_M(z_2) > P_M(z_3). \]

Pytanie Jakiej konkretnie wartości prawdopodobieństwa byśmy spodziewali się dla zdania Dzisiaj rano kupiłem w piekarni sześć bułek dla sensownego modelu języka polskiego?

Moglibyśmy sprowadzić tę definicję języka do tej „dyskretnej”, tzn. moglibyśmy przyjąć, że łańcuch $\alpha$ należy do języka wyznaczonego przez model $M$, jeśli $P_M(\alpha) > 0$.

Pytanie Czy moglibyśmy w ten sposób język nieskończony? Czy może istnieć dyskretny rozkład prawdopodobieństwa dla nieskończonego zbioru?

Co jest symbolem?

Model języka daje rozkład prawdopodobieństwa nad zbiorem łańcuchów opartym na skończonym alfabecie, tj. zbiorze symboli. W praktyce alfabet nie musi być zgodny z potocznym czy językoznawczym rozumieniem tego słowa. To znaczy alfabet może być zbiorem znaków (liter), ale modelować język możemy też przyjmując inny typ symboli: sylaby, morfemy (cząstki wyrazów) czy po prostu całe wyrazy.

Powinniśmy przy tym pamiętać, że, koniec końców, w pamięci komputera wszelkiego rodzaju łańcuchy są zapisywane jako ciągi zer i jedynek — bitów. Omówmy pokrótce techniczną stronę modelowania języka.

Kodowanie znaków

Cóż może być prostszego od pliku tekstowego?

Ala ma kota.

Komputer nic nie wie o literach.

… w rzeczywistości operuje tylko na liczbach …

… czy raczej na zerach i jedynkach …

… a tak naprawdę na ciągłym sygnale elektrycznym …

/filipg/aitech-moj-2023/media/commit/f50f638e656453dc4326e8f27255b97c614c0e40/wyk/01_Jezyk/digitalsignal.jpg

… zera i jedynki są w naszej głowie …

… co jest dziwne, naprawdę dziwne …

… ale nikt normalny się tym nie przejmuje.

Jak zakodować literę?

Zakodowanie pikseli składających się na kształtu (glyfu) litery A oczywiście nie jest dobrym pomysłem.

/filipg/aitech-moj-2023/media/commit/f50f638e656453dc4326e8f27255b97c614c0e40/wyk/01_Jezyk/raster.png

Nie, potrzebujemy arbitralnego kodowania dla wszystkich możliwych kształtów litery A (w naszych głowach): A, $\mathcal{A}$, $\mathbb{A}$, $\mathfrak{A}$ powinny otrzymać ten sam kod, powiedzmy 65 (binarnie: 1000001).

ASCII

ASCII to 7-bitowy (nie 8-bitowy!) system kodowania znaków.

  for code in range(0, 128):
    print(f'{code}: {chr(code)}')

0: 1:  2:  3:  4:  5:  6:  7:  8:  9: 10:

11: 12: 13: 14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32: 33: ! 34: " 35: # 36: $ 37: % 38: & 39: ' 40: ( 41: ) 42: * 43: + 44: , 45: - 46: . 47: / 48: 0 49: 1 50: 2 51: 3 52: 4 53: 5 54: 6 55: 7 56: 8 57: 9 58: : 59: ; 60: < 61: = 62: > 63: ? 64: @ 65: A 66: B 67: C 68: D 69: E 70: F 71: G 72: H 73: I 74: J 75: K 76: L 77: M 78: N 79: O 80: P 81: Q 82: R 83: S 84: T 85: U 86: V 87: W 88: X 89: Y 90: Z 91: [ 92: \ 93: ] 94: ^ 95: _ 96: ` 97: a 98: b 99: c 100: d 101: e 102: f 103: g 104: h 105: i 106: j 107: k 108: l 109: m 110: n 111: o 112: p 113: q 114: r 115: s 116: t 117: u 118: v 119: w 120: x 121: y 122: z 123: { 124: | 125: } 126: ~ 127: 

Jak zejść na poziom bitów?

Linux — wiersz poleceń

Linux command line:

$ echo 'Ala ma kota' > file.txt
$ hexdump -C file.txt
00000000  41 6c 61 20 6d 61 20 6b  6f 74 61 0a   |Ala ma kota.|
0000000c

Edytor tekstu (Emacs)

/filipg/aitech-moj-2023/media/commit/f50f638e656453dc4326e8f27255b97c614c0e40/wyk/01_Jezyk/hexl-mode.png

Uwaga!

  • kiedy dzieje się coś dziwnego, sprawdź co tak naprawdę jest w pliku
  • ASCII jest 7-bitowym kodowaniem (128 znaków)

    • choć zazwyczaj uzupełnionym (ang. padded) do 8 bitów
    • nie mów plik plik ASCII, kiedy masz na myśli prosty/czysty plik tekstowy (ang. plain text file)

Higiena plików tekstowych

Dobre rady

  • żadnych niepotrzebnych spacji na końcu wiersza
  • żadnych niepotrzebnych pustych wierszy na końcu pliku
  • … ale ostatni wiersz powinien zakończyć się znakiem końca wiersza
  • nie używać znaków tabulacji (zamiast tego 4 spacje)

    • wyjątek: pliki TSV
    • wyjątek: pliki Makefile
  • uwaga na niestandardowe spacje i dziwne znaki o zerowej długości

Unikod

ASCII obejmuje 128 znaków: litery alfabetu łacińskiego (właściwie angielskiego), cyfry, znaki interpunkcyjne, znaki specjalne itd.

Co z pozostałymi znakami? Polskimi ogonkami, czeskimi haczykami, francuskimi akcentami, cyrylicą, koreańskim alfabetem, chińskimi znakami, rongorongo?

워싱턴, 부산, 삼성

Rozwiązaniem jest Unikod (ang. Unicode) system, który przypisuje znakom używanym przez ludzkość liczby (kody, ang. code points).

Znak Kod ASCII Kod Unikodowy
9 57 57
a 97 97
ą - 261
ł - 322
$\aleph$ - 1488
- 7861
- 9763
😇 - 128519

UTF-8

Kody znaków są pojęciem abstrakcyjnym. Potrzebujemy konkretnego kodowania by zamienić kody w sekwencję bajtów. Najpopularniejszym kodowaniem jest UTF-8.

W kodowaniu UTF-8 znaki zapisywane za pomocą 1, 2, 3 lub 4 bajtów.

Znak Kod Unikodowy Szesnastkowo UTF-8 (binarnie)
9 57 U+0049 01001001
a 97 U+0061 01100001
ą 261 U+0105 11000100:10000101
ł 322 U+0142 11000101:10000010
$\aleph$ 1488 U+05D0 11010111:10010000
7861 U+1EB7 11100001:10111010:10110111
9763 U+2623 11100010:10011000:10100011
😇 128519 U+1f607 11110000:10011111:10011000:10000111

Źdźbło to ile bajtów w UTF-8?

Dlaczego UTF-8 jest doskonałym systemem kodowania?

  • wstecznie kompatybilny z ASCII

    • plik ASCII jest poprawnym plikiem UTF-8
  • nie zajmuje dużo miejsca

    • chyba w tekście jest dużo „dziwnych” znaków
  • proste grepowanie działa

    • grep UAM text-in-utf8.txt zadziała
    • ale nawet nie próbuj: grep SRPOL text-in-utf16.txt

Porady

  • zawsze używaj UTF-8

    • bądź asertywny! jeśli w pracy każą używać czegoś innego — rezygnuj z pracy
    • NIE używaj innych unikodowych kodowań: UTF-16, UTF-32, UCS-2
    • NIE używaj nieunikodowych systemów kodowania

      • ISO-8859-2, Windows-1250, Mazovia, IEA Świerk, …
  • uwaga na pułapki UTF-8

    • ustalenie długości napisu w znakach wymaga przejścia znak po znaku
    • jeśli napis w kodowaniu UTF-8 zajmuje 9 bajtów, ile to znaków? 3, 4, 5, 6, 7, 8 lub 9!

NIE używaj sekwencji BOM

/filipg/aitech-moj-2023/media/commit/f50f638e656453dc4326e8f27255b97c614c0e40/wyk/01_Jezyk/evil-bom.png