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

Давайте рассмотрим некоторые из распространенных проблем кодирования ML, которые задают в таких интервью:

Проблема 1:

Учитывая API rand7(), который генерирует однородное случайное целое число в диапазоне [1, 7], напишите функцию rand10(), которая генерирует однородное случайное целое число в диапазоне [1, 10]. Вы можете вызывать только API rand7(), и вам не следует вызывать какой-либо другой API. Пожалуйста, не используйте встроенный в язык случайный API.

Решение:

rand7()-1 генерирует случайное целое число от 0 до 6.

Чтобы сгенерировать случайные целые числа между [1, 10], нам нужно сгенерировать 10 равновероятных чисел или диапазон, который делится на 10 частей.

Пусть a=rand7()-1 и b= rand7(), тогда 7a+b будет генерировать случайные целые числа между [1, 49] каждое с равной вероятностью 1/49.

Если значение 7a+b находится в диапазоне [1,40], то мы можем использовать этот диапазон, чтобы поровну разделить на 10 частей размера 4, как показано ниже:

[1,4] [5,8] [9,12] [13,16] [17,20] [21,24] [25,28] [29,32] [33,36] [37,40]

Таким образом, если значение 7a+b находится между [1,4], мы возвращаем 1, если значение находится между [5,8], возвращаем 2 и так далее.

Таким образом, верните int((7a+b-1)/4)+1, если 1 ≤ 7a+b ≤ 40.

Вероятность того, что цикл while возвращает значение при каждом вызове, составляет 40/49, потому что из 49 возможных вариантов 7a+b только 40 допустимы. Таким образом, ожидаемое количество повторений цикла while для каждого вызова составляет 49/40.

Для произвольного диапазона [a, b] с помощью rand7() сначала нормализуйте диапазон до [1, b-a+1], затем найдите наименьшее P, такое что b-a+1 ≤ 7^P. Затем найдите наибольшее K такое, что K*(b-a+1) ≤ 7^P.

Пусть Q = 7^(P-1)*(rand7()-1) + 7^(P-2)*(rand7()-1) + … + 7*(rand7()-1) + rand7()

Q генерирует целые числа в диапазоне [1, 7^P].

Рассмотрим только диапазон [1, K*(b-a+1)] из значений Q. Среди этого диапазона, если мы разделим его на b-a+1 равных частей размера K каждая, то для любого целого числа в 1-я часть, т.е. [1, K], мы возвращаем 1, для 2-й части [K+1, 2K] возвращаем 2 и так далее.

Таким образом, int((Q-1)/K)+1 вернет случайное целое число в диапазоне [1, b-a+1]. Чтобы вернуться в диапазоне [a, b], добавьте к выходным данным a-1, т. е. a+int((Q-1)/K).

Ожидаемое количество раз выполнения цикла равно 7^P/K*(b-a+1).

Проблема 2:

Вам дан индексированный 0 массив положительных целых чисел w, где w[i]описывает вес индекса ith.

Вам нужно реализовать функцию pickIndex(), которая случайно выбирает индекс в диапазоне [0, w.length - 1] (включительно) и возвращает его. Вероятность выбора индекса i равна w[i] / sum(w).

Решение:

Сохраните совокупные суммы весов до индекса i для каждого индекса i в массиве. Сгенерируйте случайное целое число M из диапазона [1, sum(w)].

Найдите наименьший индекс j, такой что cum_sum[j] ≥ M. Верните j.

Поскольку массив кумулятивных сумм представляет собой отсортированный массив, мы можем выполнить бинарный поиск, чтобы найти индекс j s.t. M ≤ cum_sum[j].

Временная сложность составляет O(logN) для каждого вызова pickIndex().

Эта проблема имеет очень широкое применение, например:

  • Случайный выбор сервера балансировщиком нагрузки на основе отрицательного значения текущей нагрузки в качестве весов.
  • Случайная выборка данных из дискретного распределения вероятностей.
  • Генерация искусственных данных для имитации трафика.

Проблема 3:

Вам дано целое число n и массив уникальных целых чисел blacklist. Разработайте алгоритм для выбора случайного целого числа в диапазоне [0, n - 1], которое не находится в диапазоне blacklist. Любое целое число, которое находится в указанном диапазоне, но не в blacklist, должно быть возвращено равновероятно.

Оптимизируйте свой алгоритм так, чтобы он сводил к минимуму количество вызовов встроенной случайной функции вашего языка.

Решение:

Отсортируйте целые числа в черном списке.

Затем сгенерируйте интервалы вида [черный список[i-1]+1, черный список[i]-1], т. е. разделите весь диапазон целых чисел таким образом, чтобы исключить целые числа из черного списка

Например, если диапазон целых чисел — [1, 100], а отсортированные целые числа из черного списка — [10, 25, 40, 90], то сгенерированные диапазоны — [1,9] [11,24] [26,39], [41,89] [91,100].

Затем на основе размера каждого диапазона выберите диапазон с вероятностью = размер диапазона/сумма размеров всех диапазонов.

Это можно сделать, используя алгоритм, представленный в задаче 2.

После выбора диапазона случайным образом выберите целое число в этом диапазоне.

Вероятность выбора числа = Вероятность выбора диапазона * Вероятность выбора числа в этом диапазоне.

Если размер диапазона равен S, то вероятность = S/сумма(размер диапазонов)*1/S = 1/сумма(размер диапазонов)

Поскольку сумма (размер диапазонов) = N-len(черный список), то каждое число, не входящее в черный список, выбирается единообразно.

Проблема 4:

Дан методbiad_toss(), который генерирует 0 или 1. Он генерирует 1 с некоторой вероятностью p (не обязательно 0,5). Напишите метод, который генерирует 0 или 1 с вероятностью 0,5.

Решение:

Если мы дважды вызовем методbiad_toss(), то возможны 00, 01, 10, 11. Вероятность 00 равна (1-p)², 01 равна (1-p)*p, 10 равна p*(1- р), а 11 — это р².

Поскольку вероятности 01 и 10 равны, то есть p*(1-p), мы можем вернуть 0, если получим 01, и 1, если получим 10.

Ожидаемое количество раз выполнения цикла: 1/p*(1-p).

Проблема 5:

Дан бесконечный поток чисел. Напишите метод, возвращающий K случайных отсчетов из потока без замены.

Решение:

Это можно решить с помощью отбора проб из резервуара.

Для каждого числа в потоке мы отслеживаем его индекс.

Если размер выборочного массива меньше K, добавьте число в конец массива.

Если размер выборочного массива более чем равен K, то генерируется случайное целое число между 0 и текущим индексом числа в потоке. Если случайное целое число находится в диапазоне от 0 до K-1, то замените индекс в массиве, соответствующий случайному целому числу, на текущее число.

Нам нужно доказать, что вероятность каждого числа в потоке оказаться в выборке равна K/N, где K — длина массива выборок, а N — длина потока.

Предположим, что длина потока равна N.

Для чисел с индексом ‹ K оно является частью выборки, если оно не заменено каким-либо числом с индексом ≥ K.

Вероятность того, что число с индексом J ‹ K является частью выборки = Вероятность того, что случайное целое число, сгенерированное с индексом K, не равно J и Вероятность того, что случайное целое число, сгенерированное с индексом K+1, не равно J и … Вероятность того, что случайное целое число сгенерировано с индекс N-1 не равен J.

Вероятность того, что случайное целое число, сгенерированное с индексом K, не равно J = K/K+1

Вероятность того, что случайное целое число, сгенерированное с индексом K+1, не равно J = K+1/K+2

Вероятность того, что случайное целое число, сгенерированное с индексом N-1, не равно J = N-1/N

Таким образом

Вероятность того, что число с индексом J ‹ K является частью выборки = K/(K+1)*(K+1)/(K+2)*…*(N-1)/N=K/N.

Для чисел с индексом J ≥ K он является частью выборки, если случайное целое число X, сгенерированное между 0 и J, равно ‹ K, а случайное целое число, сгенерированное с индексом › J, не равно X.

Вероятность того, что число с индексом J ≥ K является частью выборки = Вероятность того, что случайное целое число X, сгенерированное между 0 и J, равно ‹ K, и Вероятность того, что случайное целое число, сгенерированное с индексом J+1, не равно X, и Вероятность того, что случайное целое число сгенерировано с индексом J+2 не равно X и … Вероятность того, что случайное число, сгенерированное по индексу N-1, не равно X.

Вероятность того, что случайное целое число X сгенерировано между 0 и J, равна ‹ K = K/(J+1)

Вероятность того, что случайное целое число, сгенерированное с индексом J+1, не равно X = J+1/(J+2)

Вероятность того, что случайное целое число, сгенерированное с индексом J+2, не равно X = J+2/(J+3)

Вероятность того, что случайное целое число, сгенерированное с индексом N-1, не равно X = N-1/N

Таким образом

Вероятность того, что число с индексом J ≥ K является частью выборки = K/(J+1)*(J+1)/(J+2)*(J+2)/(J+3)*…*(N- 1)/Н=К/Н.

Проблема 6:

Случайным образом перемешать массив.

Решение:

Произвольно выберите индекс j от i до Len(arr)-1. Затем поменяйте местами arr[j] на arr[i] и увеличьте i на 1.

Проблема 7:

Подбрасывайте правильную монету несколько раз, пока не выпадет две решки подряд (ЧЧ). Какова вероятность выпадения HH не более чем за N (N > 1) бросков? Можете ли вы написать рекурсивную функцию и реализацию для вычисления вероятности?

Решение:

Пусть F(n, H) — количество последовательностей длины n, заканчивающихся на HH и начинающихся с H, а F(n, T) — количество последовательностей длины n, заканчивающихся на HH и начинающихся с T.

Таким образом, F(n+1, T) = F(n, H) + F(n, T), потому что если последовательность начинается с T, то оставшаяся последовательность может начинаться либо с H, либо с T.

F(n+1, H) = F(n, T), поскольку если последовательность начинается с H, то оставшаяся последовательность может начинаться только с T (HH запрещен).

Пусть G(n+1) = F(n+1, H) + F(n+1, T) = F(n, T) + F(n, H) + F(n, T) = F(n -1, Н) + F(n-1, T) + F(n, H) + F(n, T)

G(n+1) = G(n) + G(n-1)

G(1) = 0, G(2) = 1

Вероятность P(n) = (1/2²)*G(2) + (1/2³)*G(3) + (1/2⁴)*G(4) + …. + (1/2^n)*G(n)

Проблема 8:

Вычислите nCr, где n ≥ r и 1 ≤ n ≤ 1000, 1 ≤ r ≤ n.

Решение:

nCr = n-1Cr + n-1Cr-1

Таким образом, мы будем использовать динамическое программирование для вычисления значений:

Сложность времени и пространства составляет O(N²).

Проблема 9:

Внедрите стратифицированную выборку.

Учитывая данные обучения, метки классов и долю данных проверки на класс «test_size». Напишите метод для выборки данных обучения, тестовых данных, меток обучения и тестовых меток, чтобы каждый класс имел долю данных/меток «test_size» для проверки и долю данных/меток 1-test_size для обучения.

Решение:

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

Поскольку здесь размер данных фиксирован, вместо использования выборки резервуара, как описано выше (для потоковой передачи данных), мы будем использовать другой, более чистый метод:

Сгенерируйте однородное случайное число от 0 до 1. Если оно ≤ test_size, добавьте текущие данные и метку в проверочный набор, иначе добавьте их в обучающий набор.

Но нужно ли нам сначала группировать по ярлыкам классов?

Если мы перебираем все данные, то какова вероятность того, что я выберу точку для добавления в проверочный набор s.t. вероятность на класс - test_size.

Пусть P(y=1) будет вероятностью выбора данных/метки, а P(Ci) будет вероятностью класса Ci или доли класса Ci в данных.

Тогда P(y=1) = P(y=1|C1)*P(C1) + P(y=1|C2)*P(C2) + … + P(y=1|Cm)*P(Cm ), где есть «m» классов.

Мы знаем, что P(y=1|C1) = P(y=1|C2) = …. = test_size, т. е. вероятность выбора данных/метки для проверки при условии, что метка класса равна test_size.

Таким образом, P(y=1) = test_size*(P(C1) + P(C2) + … + P(Cm)) = test_size, поскольку P(C1) + P(C2) + … + P(Cm) = 1

Таким образом, вместо группировки по метке класса мы можем перебрать все данные и выбрать данные/метку, если uniform(0,1) ≤ test_size.

Сначала это может быть не интуитивно понятным, так как может показаться, что мы сэмплируем долю test_size по всем данным, а не по классам, но обратите внимание, что распределение классов уже обрабатывается, когда я перебираю все данные. Таким образом, класс с большим количеством образцов будет иметь больше шансов.

Проблема 10:

Вам даны истинные метки и предсказанные вероятности из модели логистической регрессии для N тестовых примеров. Приблизительно вычислите баллы AUC для кривых ROC и PR.

Решение:

Для ROC нам нужны значения TPR и FPR.

TPR = TP/(Количество всех данных с истинной меткой = 1)

FPR = FP/(Количество всех данных с истинной меткой = 0)

Для каждого порога вероятности найдите значения TPR и FPR.

При заданном пороге T TP = количество данных с вероятностью ≥ T, имеющих истинную метку = 1, и FP = количество данных с вероятностью ≥ T, имеющих истинную метку = 0. Также, когда T уменьшается, TPR и FPR увеличиваются, и наоборот.

Мы можем эффективно решить эту проблему, отсортировав данные по вероятности.

После сортировки по вероятности, начиная с конца, если в индексе i истинная метка = 1, то мы увеличиваем TP на 1, в противном случае мы увеличиваем FP на 1, потому что мы предполагаем, что порог равен вероятности [i].

Чтобы вычислить AUC, мы используем формулу трапеции: 0,5 * ширина * (высота1 + высота2), где ширина — это разница в FPR между двумя последовательными пороговыми значениями, а высота1 — это TPR при пороге T (i), а высота2 — это TPR при пороге. Т(я+1).

Точно так же мы можем вычислить AUC для кривой PR.

Точность = TP/(количество данных, которые, по прогнозам, будут положительными, т. е. количество точек с вероятностью ≥ порога).

Отзыв = TP/(Количество всех данных с истинной меткой = 1).

Проблема 11:

Реализуйте базовый классификатор бинарной логистической регрессии. Он должен выполнять следующие операции:

  • Модель поезда
  • Прогнозировать данные тестирования

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

Решение:

Важные методы для рассмотрения:

  • Вычисление сигмовидных оценок
  • Расчет логистических потерь
  • Градиентный спуск (пакетный или стохастический)

Проблема 12:

Реализуйте простой классификатор дерева решений. Он должен выполнять следующие операции:

  • Модель поезда
  • Прогнозировать данные тестирования

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

Решение:

Важные методы для рассмотрения:

  • Прирост вычислительной информации
  • Разделение узла на функцию и значение функции.