Если вы еще не читали Часть 1 этого моего проекта, не стесняйтесь взглянуть. Хотя нет необходимости понимать проблемы, которые я здесь изложил, я объясняю, что такое анализ малых областей, мое вдохновение для этого проекта, то, как я собирал и подготовил свои данные, и с какими трудностями я столкнулся при этом. Работа над этим продолжается, и в ближайшие дни будет третья часть этой серии.

Эта статья была первоначально опубликована в августе 2018 года. Это слегка отредактированная копия оригинала, которая была удалена.

Здесь я расскажу о своих трудностях при моделировании данных об общественном здравоохранении, окружающей среде и образовании, которые я нашел для 59 определенных общественных округов в пяти районах Нью-Йорка.

В этом посте я буду обсуждать эти три основные темы.

  • Различные модели, которые я рассматривал и пробовал
  • Проблема переобучения ограниченными данными
  • Использование машинного обучения для интерпретации и анализа, а не для прогнозирования

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

Показатели качества воздуха и «здоровья населения» (индикаторы того, насколько сообщество процветает) в некоторых случаях также сильно коррелировали.

Наконец, расовая принадлежность коррелировала с национальными показателями ...

Моделирование данных

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

Я пошел дальше и все равно сделал это, выполнил линейную регрессию по всем своим данным и получил значение R² 0,95 (усредненное по трем выходным данным). Это все хорошо, но было две вопиющих проблемы ...

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

Вторая проблема заключается в том, что, хотя эта модель, кажется, говорит нам, что функции, включенные в модель, похоже, коррелируют с данными об образовательных результатах, когда я разделил свои данные на обучающие и тестовые наборы, независимо от их размера, я неизменно получал Значение R² больше 0,95 для данных поезда и обычно отрицательное значение для тестовых данных - максимальное значение, которое я когда-либо получал, было 0,49, и это была чистая удача, основанная на случайном разбиении данных. Так что здесь определенно возникает проблема переобучения. Имея всего 59 образцов, так как есть только 59 общинных округов, это кажется неизбежной проблемой независимо от модели.

Продвинуться по первому вопросу о взаимосвязанных характеристиках уровня образования несколькими способами. Сначала я исследовал модели, которые могут обрабатывать несколько выходов, принимая во внимание тот факт, что они связаны. Единственным многообещающим направлением, казалось, были передовые нейронные сети. Однако они были упомянуты только в исследовательских работах, а серийные модели еще не созданы, так что на данный момент это действительно только теоретическая стадия ... так что для меня это тупик.

Единственный способ обойти это - объединить три моих результата в одну партитуру. Проще всего было присвоить каждому уровню образования значение: 0 для неполной средней школы, 1 для степени средней школы и, возможно, некоторого колледжа, и 2 для степени колледжа или выше. Для каждого района я умножил каждый процент на его соответствующее значение и сложил их вместе. Это позволило создать единую, хотя и несколько абстрактную «оценку образования» для каждого округа, с которой я мог затем провести единственную линейную регрессию.

educational_attainment['edu_score'] = educational_attainment.apply(
    lambda row: row['Eduhsdegreeorsomecollege_rate'] + 2 *
    row['Educollegedegreeandhigher_rate'], axis=1)

Для всего набора данных эта линейная регрессия дала значение R² 0,96; однако он попал в ту же ловушку переобучения при разделении на комплекты поездов и тестов.

Порядковая регрессия

Не ожидая лучших результатов по проблеме переобучения, я решил попробовать некоторую порядковую регрессию с использованием модуля mord. Поэтому я использовал свои образовательные баллы, чтобы разделить данные на 4 категории, используя среднее значение и стандартное отклонение.

# Get the mean and standard deviation (indices 1 and 2 respectively)
statistics = educational_attainment['edu_score'].describe()
# Define my categories
def get_ordinal(row):
    if row.loc['edu_score'] >= (statistics[1] + statistics[2]):
        ordinal = 1
    elif row.loc['edu_score'] >= (statistics[1]):
        ordinal = 2
    elif row.loc['edu_score'] >= (statistics[1] - statistics[2]):
        ordinal = 3
    else:
        ordinal = 4
    return ordinal
    
educational_attainment['ordinal'] = educational_attainment.apply(
                               lambda row: get_ordinal(row), axis=1)

К моему удивлению, это немного лучше сказалось на чрезмерной подгонке, когда я использовал mord.LogisticIT() (порядковая логистическая регрессия с использованием варианта немедленного порога). При использовании для набора данных в целом значение R² составляло 0,93. При разделении на обучающий и тестовый наборы значение R² тестового набора данных было как минимум всегда положительным, иногда даже до 0,5 - по-прежнему нечем похвастаться, но явное улучшение.

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

Более многообещающим путем кажется использование регуляризованной линейной регрессии L2 (также известной как регрессия гребня) для вычисления важности признаков, чтобы сопровождать наши исходные корреляции с целью понимания и интерпретации данные, вместо того, чтобы пытаться создать чисто прогнозную модель. Другие модели, полезные для такого рода анализа, - это рандомизированная регрессия L1 (лассо) и регрессор дополнительных деревьев scikit-learn. Ниже приведены результаты, которые я обнаружил при реализации этих стратегий.

Регрессия гребня, рандомизированное лассо и дополнительные деревья

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

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

# Using robustly scaled columns...
def get_scores(X, Y, scale_method):
    index_tuples = []
    model_data = []
    
    for level in Y.columns:
        ridge_model = RidgeCV()
        et_model = ExtraTreesRegressor(n_estimators=50, 
            bootstrap=True)
        randL_model = RandomizedLasso()
        models = [ridge_model, et_model]   
        y = Y[level]
        for model in models:
            model.fit(X, y)
            score = model.score(X, y)
            model_name = f'{model}'.split('(')[0]
            try:
                coefs = model.coef_
            except:
                try:
                    importances = model.feature_importances_
                except:
                    importances = np.array(None)
            else:
                importances = np.absolute(coefs)
            finally:
                perm = PermutationImportance(model).fit(X, y)
                perm_importances = perm.feature_importances_
                index_tuple1 = (level, 'importances', score, 
                    model_name)
                index_tuple2 = (level, 'perm_importances', score, 
                    model_name)
        
                if importances.any():
                    index_tuples.append(index_tuple1)
                    model_data.append(importances)
                
                index_tuples.append(index_tuple2)
                model_data.append(perm_importances)

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

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