Обзор

Мы увидим, как разыгрывается аспект книги* с нуля, когда мы реализуем несколько функций строительных блоков, которые помогут нам работать над определением евклидова расстояния в коде:

  • примечание. Это глава 4, Линейная алгебра, книги Джоэла Груса «Наука о данных с нуля».

Хотя мы не сразу видим его применение, мы можем ожидать, что евклидово расстояние будет использоваться для K ближайших соседей (классификация) или K-средних (кластеризация) для поиска k ближайших точек ( "источник"). (примечание : используются и другие типы формул расстояния.)

На пути к реализации евклидова расстояния мы также реализуем сумму квадратов, которая является важной частью работы регрессии.

Таким образом, аспект этой книги с нуля работает на двух уровнях. В этой главе мы шаг за шагом строим важную формулу расстояния и суммы квадратов. Но мы также создаем инструменты, которые будем использовать в последующих главах.

Векторы

Мы начнем с реализации функций для сложения и вычитания двух векторов. Мы также создаем функцию для покомпонентной суммы списка векторов, где создается новый вектор, первый элемент которого является суммой всех первых элементов в списке и так далее.

Затем мы создаем функцию для умножения вектора на скаляр, которую мы используем для вычисления среднего значения компонента списка векторов.

Мы также создаем точечный продукт двух векторов или сумму их покомпонентного произведения, и это обобщенная версия суммы квадратов. На данный момент у нас достаточно для реализации евклидова расстояния. Давайте посмотрим на код:

Примеры векторов

Векторы — это просто список чисел:

height_weight_age = [70,170,40]
grades = [95,80,75,62]

Добавлять

Вы заметите, что мы везде вводим аннотацию в наш код. Это соглашение поддерживается автором (и, как новичку в Python, мне нравится идея четко указать тип данных для ввода и вывода функции).

from typing import List
Vector = List[float]
def add(v: Vector, w: Vector) -> Vector:
    """Adds corresponding elements"""
    assert len(v) == len(w), "vectors must be the same length"
    return [v_i + w_i for v_i, w_i in zip(v,w)]
assert add([1,2,3], [4,5,6]) == [5,7,9]

Вот еще один взгляд на то, что происходит с функцией add:

Вычесть

def subtract(v: Vector, w: Vector) -> Vector:
    """Subtracts corresponding elements"""
    assert len(v) == len(w), "vectors must be the same length"
    return [v_i - w_i for v_i, w_i in zip(v,w)]
assert subtract([5,7,9], [4,5,6]) == [1,2,3]

Это почти то же самое, что и предыдущее:

Покомпонентная сумма

def vector_sum(vectors: List[Vector]) -> Vector:
    """Sum all corresponding elements (componentwise sum)"""
    # Check that vectors is not empty
    assert vectors, "no vectors provided!"
    # Check the vectorss are all the same size
    num_elements = len(vectors[0])
    assert all(len(v) == num_elements for v in vectors), "different sizes!"
    # the i-th element of the result is the sum of every vector[i]
    return [sum(vector[i] for vector in vectors)
            for i in range(num_elements)]
assert vector_sum([[1,2], [3,4], [5,6], [7,8]]) == [16,20]

Здесь list векторов становятся одним вектором. Если вернуться к функции add, она принимает два вектора, поэтому, если мы попытаемся передать ей четыре вектора, мы получим TypeError. Итак, мы заключаем четыре вектора в list и предоставляем это в качестве аргумента для vector_sum:

Умножить вектор на число

def scalar_multiply(c: float, v: Vector) -> Vector:
    """Multiplies every element by c"""
    return [c * v_i for v_i in v]
assert scalar_multiply(2, [2,4,6]) == [4,8,12]

Одно число умножается на все числа в векторе, при этом вектор сохраняет свою длину:

Покомпонентное среднее

Это похоже на покомпонентную сумму (см. выше); список векторов становится одним вектором.

def vector_mean(vectors: List[Vector]) -> Vector: 
    """Computes the element-wise average"""
    n = len(vectors)
    return scalar_multiply(1/n, vector_sum(vectors))
assert vector_mean([ [1,2], [3,4], [5,6] ]) == [3,4]

Скалярное произведение

def dot(v: Vector, w: Vector) -> float:
    """Computes v_1 * w_1 + ... + v_n * w_n"""
    assert len(v) == len(w), "vectors must be the same length"
    return sum(v_i * w_i for v_i, w_i in zip(v,w))
assert dot([1,2,3], [4,5,6]) == 32

Здесь мы умножаем элементы, затем суммируем их результаты. Два вектора становятся одним числом (float):

Сумма площадей

def sum_of_squares(v: Vector) -> float:
    """Returns v_1 * v_1 + ... + v_n * v_n"""
    return dot(v,v)
assert sum_of_squares([1,2,3]) == 14

На самом деле sum_of_squares — это частный случай скалярного произведения:

Величина

def magnitude(v: Vector) -> float:
    """Returns  the magnitude (or length) of v"""
    return math.sqrt(sum_of_squares(v)) # math.sqrt is the square root function
assert magnitude([3,4]) == 5

С magnitude мы извлекаем квадратный корень из sum_of_squares. Это не что иное, как теорема Пифагора.

Квадратное расстояние

def squared_distance(v: Vector, w: Vector) -> float:
    """Computes (v_1 - w_1) ** 2 + ... + (v_n - w_n) ** 2"""
    return sum_of_squares(subtract(v,w))

Это расстояние между двумя векторами, возведенное в квадрат.

(Евклидово расстояние

import math
def distance(v: Vector, w: Vector) -> float:
    """Also computes the distance between v and w"""
    return math.sqrt(squared_distance(v,w))

Наконец, мы извлекаем квадратный корень из squared_distance, чтобы получить (евклидово) расстояние:

Резюме

Мы буквально создали с нуля, хотя и с некоторой помощью модуля Python math, блоки, необходимые для основных функций, которые мы ожидаем использовать позже, а именно: sum_of_squares и distance.

Приятно видеть, что эти основополагающие концепции помогают нам понимать более сложные алгоритмы машинного обучения, такие как регрессия, k-ближайших соседей (классификация), k-средних ( кластеризация) и даже коснуться теоремы Пифагора.

Далее мы рассмотрим матрицы.

***

Чтобы узнать больше о науке о данных, машинном обучении, R, Python, SQL и многом другом, найдите меня в Twitter.