52 KiB
Uczenie maszynowe – zastosowania
Zajęcia laboratoryjne
1. Python – listy składane, indeksowanie, biblioteka _NumPy
Listy składane (_List comprehension)
lista = []
for x in range(1, 11):
lista.append(x ** 2)
print(lista)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
lista = [x ** 2 for x in range(1, 11)]
print(lista)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Przypuśćmy, że mamy dane zdanie i chcemy utworzyć listę, która będzie zawierać długości kolejnych wyrazów tego zdania. Możemy to zrobić w następujący sposób:
lista = []
for i in range(1, 11):
if i % 2 == 0:
lista.append(i ** 2)
print(lista)
[4, 16, 36, 64, 100]
lista = [i ** 2 for i in range(1, 11) if i % 2 == 0]
print(lista)
[4, 16, 36, 64, 100]
list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
print(zdanie)
tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł
wyrazy = zdanie.split()
print(wyrazy)
['tracz', 'tarł', 'tarcicę', 'tak', 'takt', 'w', 'takt', 'jak', 'takt', 'w', 'takt', 'tarcicę', 'tartak', 'tarł']
dlugosci_wyrazow = []
for wyraz in wyrazy:
dlugosci_wyrazow.append(len(wyraz))
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
dlugosci_wyrazow = [len(wyraz) for wyraz in wyrazy]
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
wyrazy = zdanie.split()
dlugosci_wyrazow = []
for wyraz in wyrazy:
dlugosci_wyrazow.append(len(wyraz))
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
Możemy to też zrobić bardziej „pythonicznie”, przy użyciu list składanych:
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
dlugosci_wyrazow = [len(wyraz) for wyraz in zdanie.split()]
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
Jeżeli chcemy, żeby był sprawdzany dodatkowy warunek, np. chcemy pomijać wyraz „takt”, to wciąż możemy użyć list składanych:
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
wyrazy = zdanie.split()
dlugosci_wyrazow = [len(wyraz) for wyraz in wyrazy if wyraz != 'takt']
print(dlugosci_wyrazow)
[5, 4, 7, 3, 1, 3, 1, 7, 6, 4]
Indeksowanie
Wszystkie listy i krotki w Pythonie, w tym łańcuchy (które trakowane są jak krotki znaków), są indeksowane od 0:
napis = 'abcde'
print(napis[0]) # 'a'
print(napis[4]) # 'e'
a e
Indeksy możemy liczyć również „od końca”:
napis = 'abcde'
print(napis[-1]) # 'e' („ostatni”)
print(napis[-2]) # 'd' („drugi od końca”)
print(napis[-5]) # 'a' („piąty od końca”)
e d a
Łańcuchy możemy też „kroić na plasterki” (_slicing):
napis = 'abcde'
print(napis[1:4]) # 'bcd' („znaki od 1. włącznie do 4. wyłącznie”)
print(napis[1:2]) # 'b' (to samo co `napis[1]`)
print(napis[-3:-1]) # 'cd' (kroić można też stosując indeksowanie od końca)
print(napis[1:-1]) # 'bcd' (możemy nawet mieszać te dwa sposoby indeksowania)
print(napis[3:]) # 'de' (jeżeli koniec przedziału nie jest podany, to kroimy do samego końca łańcucha)
print(napis[:3]) # 'abc' (jeżeli początek przedziału nie jest podany, to kroimy od początku łańcucha)
print(napis[:]) # 'abcde' (kopia całego napisu)
bcd b cd bcd de abc abcde
print(napis[:])
abcde
Biblioteka _NumPy
Tablice
Głównym obiektem w NumPy jest jednorodna, wielowymiarowa tablica. Przykładem takiej tablicy jest macierz x
.
Macierz $x = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}$ można zapisać jako:
import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(A)
[[1 2 3] [4 5 6] [7 8 9]]
import numpy as np
x = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(x)
[[1 2 3] [4 5 6] [7 8 9]]
import numpy as np
y = np.array([[1, 2, 3, -4.5, 5], [10, 9, -8, -13, 0.39]])
print(y)
[[ 1. 2. 3. -4.5 5. ] [ 10. 9. -8. -13. 0.39]]
print(x.shape)
(3, 3)
print(y.shape)
(2, 5)
Korzystając z funkcji shape
, można znaleźć liczbę wierszy i kolumn macierzy:
print("Liczba kolumn: ", y.shape[0])
print("Liczba wierszy: ", y.shape[1])
Liczba kolumn: 2 Liczba wierszy: 5
z = y.reshape(5, 2)
print(z)
[[ 1. 2. ] [ 3. -4.5 ] [ 5. 10. ] [ 9. -8. ] [-13. 0.39]]
print(z.shape)
(5, 2)
w = z.reshape(2, 5)
print(w)
[[ 1. 2. 3. -4.5 5. ] [ 10. 9. -8. -13. 0.39]]
Najczęsciej używane metody tablic typu array
:
print(y.shape)
z = y.reshape((5, 2))
print(z)
print(z.shape)
(2, 5) [[ 1. 2. ] [ 3. -4.5 ] [ 5. 10. ] [ 9. -8. ] [-13. 0.39]] (5, 2)
print(z)
[[ 1. 2. ] [ 3. -4.5 ] [ 5. 10. ] [ 9. -8. ] [-13. 0.39]]
print(z.sum(axis=1)) # suma według wierszy
[ 3. -1.5 15. 1. -12.61]
print(z.sum(axis=0)) # suma według kolumn
print(x)
print(x.mean(axis=1))
[[1 2 3] [4 5 6] [7 8 9]] [2. 5. 8.]
lista = list(range(1, 11))
A = np.array(lista)
print(A)
[ 1 2 3 4 5 6 7 8 9 10]
lista = list(range(1, 11))
print(lista)
A = np.array(lista)
print(A)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [ 1 2 3 4 5 6 7 8 9 10]
A = np.arange(1, 11)
print(A)
[ 1 2 3 4 5 6 7 8 9 10]
x.sum(axis=0) # suma
array([12, 15, 18])
x.mean(axis=1)
array([2., 5., 8.])
Do tworzenia sekwencji liczbowych jako obiekty typu array
należy wykorzystać funkcję arange
.
np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.arange(5, 15)
array([ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
np.arange(5, 15, 0.1).reshape(10, 10)
array([[ 5. , 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9], [ 6. , 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9], [ 7. , 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9], [ 8. , 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9], [ 9. , 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9], [10. , 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9], [11. , 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9], [12. , 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8, 12.9], [13. , 13.1, 13.2, 13.3, 13.4, 13.5, 13.6, 13.7, 13.8, 13.9], [14. , 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9]])
Kształt tablicy można zmienić za pomocą metody reshape
:
x = np.arange(1, 13)
print(x)
y = x.reshape(3, 4)
print(y)
[ 1 2 3 4 5 6 7 8 9 10 11 12] [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]
Funkcją podobną do arange
jest linspace
, która wypełnia wektor określoną liczbą elementów z przedziału o równych automatycznie obliczonych odstępach (w arange
należy podać rozmiar kroku):
x = np.linspace(0, 2, 10)
print(x)
[0. 0.22222222 0.44444444 0.66666667 0.88888889 1.11111111 1.33333333 1.55555556 1.77777778 2. ]
Dodatkowe informacje o funkcjach NumPy uzyskuje się za pomocą polecenia help(nazwa_funkcji)
:
help(np.shape)
Help on function shape in module numpy: shape(a) Return the shape of an array. Parameters ---------- a : array_like Input array. Returns ------- shape : tuple of ints The elements of the shape tuple give the lengths of the corresponding array dimensions. See Also -------- alen ndarray.shape : Equivalent array method. Examples -------- >>> np.shape(np.eye(3)) (3, 3) >>> np.shape([[1, 2]]) (1, 2) >>> np.shape([0]) (1,) >>> np.shape(0) () >>> a = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')]) >>> np.shape(a) (2,) >>> a.shape (2,)
Tablice mogą składać się z danych różnych typów (ale tylko jednego typu danych równocześnie, stąd jednorodność).
x = np.array([1, 2, 3, 0.5])
print(x.dtype)
x = np.array([0.1, 0.2, 0.3])
print(x)
print(x.dtype)
x = np.array([1, 2, 3], dtype='float64')
print(x.dtype)
float64 [0.1 0.2 0.3] float64 float64
Tworzenie tablic składających się z samych zer lub jedynek umożliwiają funkcje zeros
oraz ones
:
x = np.zeros([3,4])
print(x)
[[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.]]
y = np.ones([3,4])
print(y)
[[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]]
print(x + y)
[[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]]
x = np.arange(12).reshape(4, 3)
print(x)
[[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]]
y = np.arange(5, 17).reshape(4, 3)
print(y)
[[ 5 6 7] [ 8 9 10] [11 12 13] [14 15 16]]
print(x * y)
[[ 0 6 14] [ 24 36 50] [ 66 84 104] [126 150 176]]
A = np.arange(8).reshape(2, 4)
print(A)
[[0 1 2 3] [4 5 6 7]]
B = np.arange(1, 13).reshape(4, 3)
print(B)
[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]]
print(A.shape, B.shape)
(2, 4) (4, 3)
C = np.matmul(A, B)
print(C)
print(C.shape)
[[ 48 54 60] [136 158 180]] (2, 3)
A * B
[1;31m---------------------------------------------------------------------------[0m [1;31mValueError[0m Traceback (most recent call last) [1;32m<ipython-input-102-a4cedde81ed0>[0m in [0;36m<module>[1;34m[0m [1;32m----> 1[1;33m [0mA[0m [1;33m*[0m [0mB[0m[1;33m[0m[1;33m[0m[0m [0m [1;31mValueError[0m: operands could not be broadcast together with shapes (2,4) (4,3)
np.matmul(A, C)
[1;31m---------------------------------------------------------------------------[0m [1;31mValueError[0m Traceback (most recent call last) [1;32m<ipython-input-103-89ae99960b6e>[0m in [0;36m<module>[1;34m[0m [1;32m----> 1[1;33m [0mnp[0m[1;33m.[0m[0mmatmul[0m[1;33m([0m[0mA[0m[1;33m,[0m [0mC[0m[1;33m)[0m[1;33m[0m[1;33m[0m[0m [0m [1;31mValueError[0m: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 4)
Podstawowe operacje arytmetyczne
Operatory arytmetyczne na tablicach w NumPy działają element po elemencie.
A = np.array([[1, 2, 3], [1, 2, 3]])
B = np.array([[4, 5, 6], [7, 8, 9]])
print(A)
[[1 2 3] [1 2 3]]
print(B)
[[4 5 6] [7 8 9]]
print(A - B)
[[-3 -3 -3] [-6 -6 -6]]
print(B - A)
[[3 3 3] [6 6 6]]
print(A * B)
[[ 4 10 18] [ 7 16 27]]
np.matmul(A, B)
[1;31m---------------------------------------------------------------------------[0m [1;31mValueError[0m Traceback (most recent call last) [1;32m<ipython-input-109-04c68bb92949>[0m in [0;36m<module>[1;34m[0m [1;32m----> 1[1;33m [0mnp[0m[1;33m.[0m[0mmatmul[0m[1;33m([0m[0mA[0m[1;33m,[0m [0mB[0m[1;33m)[0m[1;33m[0m[1;33m[0m[0m [0m [1;31mValueError[0m: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)
print(A.shape)
B = np.array([[1, 2, 3, 4], [9, 8, 7, 6], [2, 4, 6, 7]])
print(B.shape)
print()
print(A)
print(B)
C = np.matmul(A, B)
print(C)
print()
print(A.shape, B.shape)
print(C.shape)
(2, 3) (3, 4) [[1 2 3] [1 2 3]] [[1 2 3 4] [9 8 7 6] [2 4 6 7]] [[25 30 35 37] [25 30 35 37]] (2, 3) (3, 4) (2, 4)
np.dot(A, B)
array([[25, 30, 35, 37], [25, 30, 35, 37]])
import numpy as np
a = np.array([3, 4, 5])
b = np.ones(3)
print(a - b)
[2. 3. 4.]
Za mnożenie macierzy odpowiada funkcja dot
(nie operator *
):
a = np.array([[1, 2], [3, 4]])
print(a)
[[1 2] [3 4]]
b = np.array([[1, 2], [3, 4]])
print(b)
[[1 2] [3 4]]
a * b
array([[ 1, 4], [ 9, 16]])
np.dot(a,b)
array([[ 7, 10], [15, 22]])
Przykłady innych operacji dodawania i mnożenia:
a = np.zeros((2, 2), dtype='float')
a += 5
a
array([[5., 5.], [5., 5.]])
a *= 5
a
array([[25., 25.], [25., 25.]])
a + a
array([[50., 50.], [50., 50.]])
Sklejanie tablic:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])
np.hstack([a, b, c])
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
np.vstack([a, b, c])
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Typowe funkcje matematyczne:
x = np.arange(1, 5)
np.sqrt(x) * np.pi
array([3.14159265, 4.44288294, 5.44139809, 6.28318531])
2**4
16
np.power(2, 4)
16
np.log(np.e)
1.0
x = np.arange(5)
x.max() - x.min()
4
Indeksy i zakresy
Tablice jednowymiarowe zachowują sie podobnie do zwykłych list pythonowych.
a = np.arange(10)
a[2:4]
array([2, 3])
a[:10:2] # elementy do 10., co drugi element
array([0, 2, 4, 6, 8])
a[::-1] # wszytkie elementy tablicy `a`, ale w odwróconej kolejności
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
Tablice wielowymiarowe mają po jednym indeksie na wymiar:
x = np.arange(12).reshape(3, 4)
x
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
x[2, 3] # wiersz 2, kolumna 3
11
x[:, 1] # kolumna 1
array([1, 5, 9])
x[1, :] # wiersz 1
array([4, 5, 6, 7])
x[1:3, :] # wiersze od 1. włącznie do 3. wyłącznie
array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]])
Warunki
Warunki pozwalają na selekcję elementów tablicy.
a = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
a[a > 1]
array([2, 2, 2, 3, 3, 3])
a[a == 3]
array([3, 3, 3])
np.where(a < 3)
(array([0, 1, 2, 3, 4, 5], dtype=int64),)
np.where(a < 3)[0]
array([0, 1, 2, 3, 4, 5], dtype=int64)
np.where(a > 9)
(array([], dtype=int64),)
Pętle i wypisywanie
for row in x:
print(row)
[0 1 2 3] [4 5 6 7] [ 8 9 10 11]
for row in x:
for element in row:
print(element)
0 1 2 3 4 5 6 7 8 9 10 11
# Operacja `.flat` "spłaszcza" macierz
for element in x.flat:
print(element)
0 1 2 3 4 5 6 7 8 9 10 11
Liczby losowe
np.random.randint(0, 10, 5) # Tablica złożona z 5 liczb całkowitych wylosowanych z zakresu od 0 do 10
array([9, 7, 1, 6, 5])
np.random.normal(0, 1, 5) # Tablica złożona z 5 liczb wylosowanych z rozkładu normalnego (0, 1)
array([-0.5127886 , -0.4507017 , -1.06312973, 1.22150748, 0.27790976])
np.random.uniform(0, 2, 5) # Tablica złożona z 5 liczb wylosowanych z rozkładu jednostajnego na przedziale (0, 1)
array([1.34207418, 1.70197756, 1.16472435, 0.57619858, 1.22382641])
Macierze
NumPy jest pakietem wykorzystywanym do obliczeń w dziedzinie algebry liniowej, co jeszcze szczególnie przydatne w uczeniu maszynowym.
Wektor o wymiarach $1 \times N$ $$ x = \begin{pmatrix} x_{1} \\ x_{2} \\ \vdots \\ x_{N} \end{pmatrix} $$
i jego transpozycję $x^\top = (x_{1}, x_{2},\ldots,x_{N})$ można wyrazić w Pythonie w następujący sposób:
Transpozycja to operacja zamiany wierszy na kolumny i na odwrót.
A = np.arange(9).reshape(3, 3)
print(A)
[[0 1 2] [3 4 5] [6 7 8]]
print(A.T)
[[0 3 6] [1 4 7] [2 5 8]]
import numpy as np
x = np.array([[1, 2, 3]]).T
x.shape
(3, 1)
xt = x.T
xt.shape
(1, 3)
Macierz kolumnowa w NumPy.
$$X =
\begin{pmatrix}
3 \\
4 \\
5 \\
6
\end{pmatrix}$$
x = np.array([[3,4,5,6]]).T
x
array([[3], [4], [5], [6]])
Macierz wierszowa w NumPy. $$ X = \begin{pmatrix} 3 & 4 & 5 & 6 \end{pmatrix}$$
x = np.array([[3,4,5,6]])
x
array([[3, 4, 5, 6]])
Oprócz obiektów typu array
istnieje wyspecjalizowany obiekt matrix
, dla którego operacje *
(mnożenie) oraz **-1
(odwracanie) są określone w sposób właściwy dla macierzy (w przeciwieństwie do operacji elementowych dla obiektów array
).
x = np.array([1,2,3,4,5,6,7,8,9]).reshape(3,3)
print(x)
[[1 2 3] [4 5 6] [7 8 9]]
y = np.array([4,6,3,8,7,1,3,0,3]).reshape(3,3)
print(y)
[[4 6 3] [8 7 1] [3 0 3]]
X = np.matrix(x)
Y = np.matrix(y)
print(x * y) # Tablice np.array mnożone są element po elemencie
[[ 4 12 9] [32 35 6] [21 0 27]]
print(X * Y) # Macierze np.matrix mnożone są macierzowo
[[ 29 20 14] [ 74 59 35] [119 98 56]]
print(np.matmul(x, y))
[[ 29 20 14] [ 74 59 35] [119 98 56]]
Wyznacznik macierzy
a = np.array([[3,-9],[2,5]])
np.linalg.det(a)
33.000000000000014
Macierz odwrotna
A = np.array([[-4,-2],[5,5]])
A
array([[-4, -2], [ 5, 5]])
invA = np.linalg.inv(A)
invA
array([[-0.5, -0.2], [ 0.5, 0.4]])
np.round(np.dot(A, invA))
array([[1., 0.], [0., 1.]])
(ponieważ $AA^{-1} = A^{-1}A = I$).
Wartości i wektory własne
a = np.diag((1, 2, 3))
a
array([[1, 0, 0], [0, 2, 0], [0, 0, 3]])
w, v = np.linalg.eig(a)
print(w) # wartości własne
print(v) # wektory własne
[1. 2. 3.] [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]