Машинное обучение — это область исследования, которая позволяет машинам обучаться и повышать свою производительность автоматически, без явного программирования. Это подмножество искусственного интеллекта (ИИ), основное внимание в котором уделяется обучению алгоритмов выполнению задач на основе входных данных. Область машинного обучения быстро развивается, и новичкам может быть сложно не отставать от используемой терминологии и концепций. В этом сообщении блога мы рассмотрим некоторые основные термины и концепции машинного обучения. Понимая эти концепции, новички могут заложить прочную основу для изучения более сложных тем машинного обучения. Мы рассмотрим такие вещи, как контролируемое и неконтролируемое обучение, регрессию, классификацию, нейронные сети, сверточные нейронные сети и многое другое.
медиана
Медиана — это среднее значение в отсортированном наборе данных. Если набор данных имеет нечетное количество значений, медианой является среднее значение. Если набор данных имеет четное количество значений, медиана представляет собой среднее значение двух средних значений.
import numpy as np data = np.array([1, 2, 3, 4, 5]) median = np.median(data) print(median)
Режим
Мода — это значение, которое чаще всего встречается в наборе данных.
from scipy import stats data = np.array([1, 2, 3, 4, 5, 5]) mode = stats.mode(data) print(mode)
Иметь в виду
Среднее значение — это среднее значение набора данных. Он рассчитывается путем суммирования всех значений в наборе данных и последующего деления на общее количество значений.
import numpy as np data = np.array([1, 2, 3, 4, 5]) mean = np.mean(data) print(mean)
Среднеквадратичное отклонение
Стандартное отклонение — это мера того, насколько разбросаны значения в наборе данных. Он рассчитывается, сначала находя среднее значение набора данных, затем находя разницу между каждым значением и средним значением, возводя в квадрат различия, находя среднее значение квадратов различий и, наконец, извлекая квадратный корень из среднего.
import numpy as np data = np.array([1, 2, 3, 4, 5]) std_dev = np.std(data) print(std_dev)
Дисперсия
Дисперсия — это статистическая мера, которая описывает, насколько отдельные точки данных разбросаны вокруг среднего или ожидаемого значения набора данных. Он измеряет степень разброса или дисперсии набора данных по его среднему значению. Другими словами, дисперсия — это среднее квадратов отличий от среднего.
В Python мы можем рассчитать дисперсию, используя библиотеку numpy
:
import numpy as np # calculate variance data = np.array([1, 2, 3, 4, 5]) variance = np.var(data) print(variance) # Output: 2.0
процентили
В статистике процентиль — это мера, используемая для обозначения значения, ниже которого падает данный процент наблюдений в группе наблюдений. Например, 25-й процентиль — это значение, ниже которого попадают 25 процентов наблюдений, а 50-й процентиль — это значение, ниже которого попадают 50 процентов наблюдений, также известное как медиана. Процентили обычно используются в анализе данных, чтобы понять распределение данных и выявить любые выбросы.
Для вычисления процентилей в Python вы можете использовать функцию numpy.percentile
. Например, следующий код вычисляет 25-й, 50-й и 75-й процентили заданного массива data
:
import numpy as np data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) p25 = np.percentile(data, 25) p50 = np.percentile(data, 50) p75 = np.percentile(data, 75) print(f"25th percentile: {p25}, 50th percentile: {p50}, 75th percentile: {p75}")
Распределение данных
Распределение данных относится к шаблону или форме набора данных после того, как они были проанализированы и нанесены на график. Он описывает, как часто встречаются значения в наборе данных и насколько разбросаны или сгруппированы значения. Распределение набора данных является важным аспектом статистического анализа, поскольку оно помогает нам понять характеристики данных и сделать значимые выводы о лежащей в их основе генеральной совокупности.
Некоторые распространенные типы распределения данных включают в себя:
- Нормальное распределение. Это колоколообразная кривая, указывающая на симметричное распределение данных относительно среднего значения.
- Асимметричное распределение. Это распределение имеет более длинный хвост на одной стороне кривой, что указывает на то, что данные распределены неравномерно.
- Бимодальное распределение: это распределение имеет два пика или режима, что указывает на наличие двух отдельных групп в наборе данных.
Распределение данных можно анализировать с использованием различных статистических показателей, таких как среднее значение, медиана, мода, дисперсия и стандартное отклонение. Понимание распределения данных может помочь в выборе подходящих статистических методов для анализа данных, обнаружения выбросов и прогнозирования.
Нормальное распределение данных
Нормальное распределение, также известное как распределение Гаусса или колоколообразная кривая, представляет собой непрерывное распределение вероятностей, симметричное относительно среднего значения. При нормальном распределении примерно 68 % данных находятся в пределах одного стандартного отклонения от среднего, 95 % — в пределах двух стандартных отклонений и 99,7 % — в пределах трех стандартных отклонений.
В Python мы можем сгенерировать случайную выборку с нормальным распределением, используя функцию random.normal()
библиотеки numpy
. Вот пример фрагмента кода, который генерирует случайную выборку с нормальным распределением, строит гистограмму данных и накладывает теоретическую кривую нормального распределения:
import numpy as np import matplotlib.pyplot as plt # Generate a random sample with mean=0 and standard deviation=1 mu, sigma = 0, 1 # mean and standard deviation sample_size = 1000 sample = np.random.normal(mu, sigma, sample_size) # Plot histogram of the sample data count, bins, ignored = plt.hist(sample, 30, density=True) # Overlay the normal distribution curve x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100) plt.plot(x, 1/(sigma * np.sqrt(2 * np.pi)) * np.exp(- (x - mu)**2 / (2 * sigma**2)), linewidth=2, color='r') plt.show()
Этот код сгенерирует случайную выборку размером 1000 со средним значением 0 и стандартным отклонением 1 и построит гистограмму данных с 30 ячейками. Затем он наложит теоретическую кривую нормального распределения поверх гистограммы.
Линейная регрессия
Линейная регрессия — это контролируемый алгоритм машинного обучения, используемый для прогнозирования непрерывной переменной результата на основе одной или нескольких входных переменных-предикторов. Предполагается, что существует линейная зависимость между входными и выходными переменными. Другими словами, он пытается найти прямую линию, которая лучше всего соответствует заданным точкам данных.
Общая формула линейной регрессии может быть представлена как:
y = mx + b
где y — прогнозируемое значение переменной результата, m — наклон линии, x — значение входной переменной-предиктора, а b — точка пересечения линии по оси y.
В Python вы можете реализовать линейную регрессию, используя популярную библиотеку машинного обучения scikit-learn. Вот пример кода:
import numpy as np from sklearn.linear_model import LinearRegression # Define the input and output variables X = np.array([[1], [2], [3], [4], [5]]) y = np.array([2, 4, 5, 4, 5]) # Create a linear regression model model = LinearRegression() # Fit the model to the data model.fit(X, y) # Predict the outcome variable for a new input value new_X = np.array([[6]]) predicted_y = model.predict(new_X) print(predicted_y)
Этот код создает модель линейной регрессии, согласовывает ее с входными и выходными переменными, а затем прогнозирует значение выходной переменной для нового входного значения.
Полиномиальная регрессия
Полиномиальная регрессия — это тип регрессионного анализа, в котором взаимосвязь между независимой переменной x и зависимой переменной y моделируется как полином n-й степени. Это расширение линейной регрессии, которое моделирует связь между независимой переменной и зависимой переменной в виде прямой линии.
Полиномиальная регрессия может отображать более сложные взаимосвязи между независимыми и зависимыми переменными, а также может подгонять кривые к данным, которые не могут быть отображены прямой линией. Степень полинома определяет сложность модели.
В Python полиномиальную регрессию можно выполнить с помощью функции polyfit()
из библиотеки NumPy. Степень полинома задается с помощью параметра deg
.
Вот пример подгонки полинома второй степени к набору данных:
import numpy as np import matplotlib.pyplot as plt # Generate some sample data x = np.array([1, 2, 3, 4, 5]) y = np.array([3, 5, 7, 9, 11]) # Fit a second-degree polynomial coefficients = np.polyfit(x, y, deg=2) # Create a polynomial function from the coefficients poly_func = np.poly1d(coefficients) # Evaluate the function at some points x_eval = np.linspace(0, 6, 100) y_eval = poly_func(x_eval) # Plot the original data and the fitted curve plt.scatter(x, y) plt.plot(x_eval, y_eval) plt.show()
Этот код генерирует набор выборочных данных, подбирает полином второй степени к данным с помощью polyfit()
, создает полиномиальную функцию из коэффициентов, оценивает функцию в некоторых точках и строит исходные данные и подобранную кривую.
Множественная регрессия
Множественная регрессия — это статистический метод, используемый для прогнозирования результата зависимой переменной на основе значений нескольких независимых переменных. Другими словами, он используется для оценки взаимосвязи между зависимой переменной и двумя или более независимыми переменными.
При множественной регрессии зависимая переменная прогнозируется как функция двух или более независимых переменных. Уравнение для модели множественной регрессии представлено как:
Y = b0 + b1X1 + b2X2 + … + bn*Xn
где Y — зависимая переменная, X1, X2, …, Xn — независимые переменные, b0 — точка пересечения, а b1, b2, …, bn — коэффициенты для независимых переменных.
Чтобы найти коэффициенты, множественная регрессия использует метод, называемый обычными наименьшими квадратами (OLS), который минимизирует сумму квадратов разностей между фактическими и прогнозируемыми значениями зависимой переменной. Коэффициенты представляют собой изменение зависимой переменной для каждого единичного изменения независимой переменной при неизменности всех остальных независимых переменных.
В Python множественная регрессия может быть выполнена с помощью библиотеки statsmodels
. Вот пример кода для множественной регрессии:
import statsmodels.api as sm import pandas as pd # load data data = pd.read_csv('data.csv') # define dependent and independent variables X = data[['independent_variable_1', 'independent_variable_2', ...]] Y = data['dependent_variable'] # add constant to independent variables X = sm.add_constant(X) # fit multiple regression model model = sm.OLS(Y, X).fit() # print model summary print(model.summary())
В этом коде data
— это кадр данных Pandas, содержащий независимые и зависимые переменные. Независимые переменные выбираются по именам их столбцов и сохраняются в переменной X
, а зависимая переменная сохраняется в переменной Y
. Функция add_constant
из statsmodels.api
добавляет к независимым переменным константу, необходимую для метода МНК. Затем модель множественной регрессии подгоняется с помощью функции sm.OLS
, а сводка модели печатается с использованием метода summary
подобранного объекта модели.
Шкала
В машинном обучении под масштабированием понимается процесс преобразования данных в стандартный масштаб. Это важно, поскольку может помочь повысить производительность алгоритмов машинного обучения, чувствительных к масштабу входных данных.
Существует несколько методов масштабирования данных, в том числе:
- Стандартизация. Этот метод масштабирует данные таким образом, чтобы среднее значение равнялось 0, а стандартное отклонение равнялось 1. Он рассчитывается по формуле: (x — среднее значение) / стандартное отклонение.
- Min-Max Scaling: этот метод масштабирует данные таким образом, чтобы они находились между заданным минимальным и максимальным значением, обычно от 0 до 1. Он рассчитывается по формуле: (x — min) / (max – мин).
- Надежное масштабирование. Этот метод масштабирует данные, вычитая медиану и масштабируя ее в соответствии с межквартильным диапазоном (IQR). Он рассчитывается по формуле: (x — медиана) / IQR.
Вот пример того, как реализовать масштабирование стандартизации с помощью Python и библиотеки NumPy:
import numpy as np # Create an array of data data = np.array([[1, 2], [3, 4], [5, 6], [7, 8]]) # Calculate the mean and standard deviation of the data mean = np.mean(data, axis=0) std_dev = np.std(data, axis=0) # Scale the data using standardization scaled_data = (data - mean) / std_dev print(scaled_data)
Это выведет:
[[-1.34164079 -1.34164079] [-0.4472136 -0.4472136 ] [ 0.4472136 0.4472136 ] [ 1.34164079 1.34164079]]
В этом случае для масштабирования признаков используется метод StandardScaler
. Сначала мы создаем экземпляр класса StandardScaler
, а затем вызываем для него метод fit_transform()
, передавая данные объекта в качестве аргумента. Этот метод вычисляет среднее значение и стандартное отклонение данных объектов и соответствующим образом масштабирует данные. Наконец, масштабированные данные возвращаются в виде массива NumPy.
Стандартизация
StandardScaler
— это метод предварительной обработки данных в машинном обучении, который масштабирует данные так, чтобы среднее значение равнялось 0, а стандартное отклонение — 1. Это форма нормализации, которая используется для приведения признаков к одному масштабу, чтобы каждый признак вносил равный вклад в модель.
В Python StandardScaler
доступен в модуле sklearn.preprocessing
. Чтобы использовать его, сначала создайте экземпляр StandardScaler
, а затем подгоните и преобразуйте данные с помощью метода fit_transform()
. Вот пример:
from sklearn.preprocessing import StandardScaler import numpy as np # create some example data data = np.array([[1, 2], [3, 4], [5, 6]]) # create an instance of StandardScaler scaler = StandardScaler() # fit and transform the data scaled_data = scaler.fit_transform(data) # print the scaled data print(scaled_data)
Это выведет масштабированные данные:
[[-1.22474487 -1.22474487] [ 0. 0. ] [ 1.22474487 1.22474487]]
Поезд/тест
В машинном обучении train/test
является распространенным подходом к оценке производительности модели. Основная идея состоит в том, чтобы разделить набор данных на два подмножества: одно для обучения модели, а другое — для тестирования модели. Обучающий набор используется для подгонки модели, а тестовый набор используется для оценки производительности модели на новых, невидимых данных. Цель состоит в том, чтобы построить модель, которая хорошо обобщает новые данные, а это означает, что она может делать точные прогнозы на основе данных, которые она никогда раньше не видела.
Разделение между обучающими и тестовыми наборами может варьироваться, но обычно используется 80% данных для обучения и 20% для тестирования. Разделение должно быть рандомизированным, чтобы убедиться, что обучающие и тестовые наборы репрезентативны для общего набора данных.
Вот пример того, как разделить данные на наборы для обучения и тестирования с помощью scikit-learn в Python:
from sklearn.model_selection import train_test_split import numpy as np # create some data X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) y = np.array([0, 1, 0]) # split the data into training and testing sets X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) print("Training data:") print(X_train) print(y_train) print("Testing data:") print(X_test) print(y_test)
В этом примере мы сначала создаем некоторые данные, которые состоят из матрицы 3x3 X
и одномерного массива y
. Затем мы используем функцию train_test_split
из scikit-learn, чтобы разделить данные на наборы для обучения и тестирования. Параметр test_size
указывает долю данных, которые будут использоваться для тестирования (в данном случае для тестирования используется 20% данных), а параметр random_state
обеспечивает воспроизводимое разделение данных. Полученные наборы для обучения и тестирования сохраняются в переменных X_train
, y_train
, X_test
и y_test
, которые затем выводятся на консоль.
Древо решений
Дерево решений — это инструмент поддержки принятия решений, который использует древовидный график или модель решений и их возможных последствий, включая результаты случайных событий, затраты ресурсов и полезность. Это простой, но мощный алгоритм классификации, который можно использовать как для задач регрессии, так и для задач классификации в машинном обучении. Деревья решений строятся с использованием иерархической структуры узлов, представляющих решение или случайное событие, и соединяющих их ребер. Каждый внутренний узел представляет проверку атрибута, каждая ветвь представляет собой результат проверки, а каждый конечный узел представляет собой метку класса. Дерево строится путем рекурсивного разделения обучающих данных на подмножества с использованием набора правил разделения, которые максимизируют прирост информации или минимизируют примеси в каждом узле. После того, как дерево построено, его можно использовать для прогнозирования новых данных путем обхода дерева от корня до конечного узла на основе значений атрибутов новых данных.
Вот пример построения классификатора дерева решений с использованием scikit-learn в Python:
from sklearn.datasets import load_iris from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # Load the iris dataset iris = load_iris() # Split the data into training and testing sets X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42) # Create a decision tree classifier and fit it to the training data clf = DecisionTreeClassifier(random_state=42) clf.fit(X_train, y_train) # Make predictions on the testing data y_pred = clf.predict(X_test) # Calculate the accuracy of the classifier accuracy = accuracy_score(y_test, y_pred) print("Accuracy:", accuracy)
В этом примере мы загружаем набор данных радужной оболочки и разделяем его на наборы для обучения и тестирования, используя функцию train_test_split
из scikit-learn. Затем мы создаем объект DecisionTreeClassifier
и подгоняем его к обучающим данным с помощью метода fit
. Наконец, мы используем метод predict
, чтобы делать прогнозы по данным тестирования и вычислять точность классификатора, используя функцию accuracy_score
из scikit-learn.
Слой нейронной сети
В нейронной сети слой относится к набору взаимосвязанных нейронов, предназначенных для выполнения определенных вычислений над входными данными. В типичной архитектуре нейронной сети входные данные проходят через один или несколько слоев нейронов, где каждый слой отвечает за выполнение определенного набора вычислений. Выход одного слоя становится входом следующего слоя, и этот процесс продолжается до тех пор, пока не будет получен окончательный результат. Каждый слой в нейронной сети может иметь свой собственный набор параметров, таких как веса и смещения, которые оптимизируются в процессе обучения для повышения точности прогнозов модели.
Нейрон
Нейрон в нейронной сети — это математическая функция, которая получает один или несколько входных данных и генерирует один выходной сигнал. Это основной строительный блок искусственных нейронных сетей, вдохновленный биологическими нейронами человеческого мозга.
В нейронной сети нейрон обычно представляется как узел, который принимает один или несколько входных данных, выполняет некоторые математические операции с этими входными данными и генерирует выходные данные. Затем выходные данные одного нейрона передаются в качестве входных данных одному или нескольким другим нейронам в сети.
Например, простой нейрон может принять два входных значения (x1 и x2), умножить их на веса (w1 и w2) и добавить смещение (b) перед тем, как передать результат через функцию активации (f) для генерации выходного значения (y ):
y = f(w1*x1 + w2*x2 + b)
Здесь функция активации используется для введения нелинейности в выходные данные, что позволяет сети изучать более сложные взаимосвязи между входными и выходными данными.
Нейроны обычно располагаются в слоях в нейронной сети, где каждый слой обрабатывает выходные данные предыдущего слоя. Первый слой — это входной слой, который получает необработанные входные данные, а последний слой — это выходной слой, который генерирует конечный результат сети.
Вес и уклон
В нейронных сетях веса и смещения используются для прогнозирования. Веса представляют силу связей между нейронами, а смещения представляют перехваты. Веса и смещения обновляются в процессе обучения, чтобы свести к минимуму ошибку между прогнозируемым результатом и фактическим результатом.
Например, в простой нейронной сети с одним входным и одним выходным слоями входной слой посылает сигналы выходному слою через набор весов. Каждый вес представляет силу связи между нейроном входного слоя и нейроном выходного слоя. Смещения в выходном слое добавляются к взвешенной сумме входных данных для создания окончательного результата.
Во время обучения сети предоставляется набор входных данных и соответствующих целевых выходных данных. Веса и смещения корректируются итеративно, чтобы свести к минимуму разницу между прогнозируемыми выходными данными и целевыми выходными данными. Этот процесс известен как обратное распространение.
Обратное распространение
Обратное распространение — это широко используемый алгоритм в нейронных сетях для вычисления градиента функции ошибок по отношению к весам сети. Алгоритм используется в процессе обучения для обновления весов и смещений нейронной сети, чтобы свести к минимуму ошибку между прогнозируемым выходом и фактическим выходом.
Алгоритм обратного распространения начинается с выходного слоя и вычисляет ошибку между прогнозируемым выходом и фактическим выходом. Затем эта ошибка распространяется обратно по слоям сети, а веса и смещения каждого нейрона корректируются пропорционально их вкладу в ошибку. Этот процесс повторяется для каждого обучающего примера, пока ошибка не будет минимизирована.
Алгоритм обратного распространения основан на цепном правиле исчисления и является эффективным с вычислительной точки зрения способом вычисления градиента функции ошибок по отношению к весам и смещениям сети. Это один из ключевых компонентов многих архитектур нейронных сетей, который используется в таких приложениях, как распознавание изображений, распознавание речи и обработка естественного языка.
Вот пример кода для реализации обратного распространения в нейронной сети с использованием библиотеки Keras:
from keras.models import Sequential from keras.layers import Dense # Create a neural network with one input layer, one hidden layer, and one output layer model = Sequential() model.add(Dense(10, input_dim=8, activation='relu')) model.add(Dense(1, activation='sigmoid')) # Compile the model model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) # Train the model using backpropagation model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))
CNN — Сверточная нейронная сеть
CNN расшифровывается как Convolutional Neural Network, тип нейронной сети, используемый в основном для классификации изображений и распознавания объектов. Это архитектура глубокого обучения, которая предназначена для автоматического и адаптивного изучения пространственных иерархий объектов из входных данных. CNN особенно хороши для изучения пространственных отношений в данных и могут автоматически учиться обнаруживать такие функции, как края, формы и текстуры. Они состоят из нескольких слоев свертки, пула и полносвязных слоев, которые обучаются с использованием обратного распространения. Сверточные слои выполняют операцию свертки входных данных для извлечения объектов, объединяющие слои понижают дискретизацию карт объектов, а полносвязные слои классифицируют выходные данные. CNN использовались в широком спектре приложений, включая классификацию изображений и видео, обнаружение объектов и распознавание лиц.
Слой
В сверточных нейронных сетях (CNN) слой представляет собой набор нейронов, которые выполняют определенную операцию над входными данными. CNN обычно состоят из нескольких слоев, каждый из которых выполняет определенную функцию при обработке входных данных.
В CNN есть несколько типов слоев, включая сверточные слои, объединяющие слои и полносвязные слои.
- Сверточные слои применяют фильтры для извлечения признаков из входного изображения. Эти фильтры обычно представляют собой небольшие квадратные матрицы весов, которые сворачиваются по входному изображению для создания набора выходных карт объектов.
- Слои объединения используются для уменьшения пространственного размера карт объектов, созданных с помощью сверточных слоев. Они понижают разрешение входного изображения, беря максимальное или среднее значение группы соседних пикселей в каждой карте объектов.
- Полносвязные слои используются для выполнения классификации или регрессии на выходе сверточных слоев и слоев объединения. Каждый нейрон в полностью связанном слое связан с каждым нейроном в предыдущем слое, и веса и смещения этих соединений изучаются в процессе обучения.
Порядок и количество слоев в CNN определяются конкретной решаемой задачей и характеристиками входных данных. Выходные данные последнего слоя передаются через функцию softmax для получения распределения вероятностей по возможным классам, которое затем можно использовать для прогнозирования.
Функции активации
Функции активации являются неотъемлемой частью нейронных сетей и используются для введения нелинейности в модель. Без функций активации нейронные сети были бы ограничены линейными функциями и, следовательно, не могли бы моделировать сложные паттерны.
Функция активации принимает взвешенную сумму входных данных и смещений, применяет к ней математическую функцию и выводит значение активации. Затем это значение активации передается следующему слою нейронов. Наиболее часто используемые функции активации:
- Сигмовидная функция — сопоставляет любое входное значение со значением от 0 до 1.
- Функция ReLU — устанавливает все отрицательные входные значения в 0 и оставляет все положительные значения без изменений.
- Функция Softmax — сопоставляет входные значения с распределением вероятностей по классам.
Существует много других функций активации, таких как функция гиперболического тангенса (tanh), которая похожа на сигмовидную функцию, но отображает входные значения в диапазоне от -1 до 1. Выбор функции активации зависит от типа решаемой задачи. и структуру сети.
Заключение
В заключение, понимание основных терминов и концепций машинного обучения необходимо любому новичку. Некоторые из наиболее фундаментальных концепций включают распределение данных, регрессию, масштабирование и нейронные сети. Также важно иметь общее представление о доступных библиотеках и инструментах, таких как NumPy и TensorFlow. Хотя поначалу это может показаться ошеломляющим, знакомство с этими терминами и концепциями обеспечит прочную основу для дальнейшего изучения и роста в области машинного обучения.
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу