Использование intrinsics в их собственной инициализации [duplicate]

Однострунная кодировка требует бит больше, чем преобразование значений в переменные индикатора. Обычно процесс ML требует, чтобы вы несколько раз применяли это кодирование для проверки или тестирования наборов данных и применяли модель, которую вы создаете, к наблюдаемым в режиме реального времени данным. Вы должны сохранить отображение (преобразование), которое использовалось для построения модели. Хорошее решение будет использовать DictVectorizer или LabelEncoder (за ним следует get_dummies. Вот функция, которую вы можете использовать:

def oneHotEncode2(df, le_dict = {}):
    if not le_dict:
        columnsToEncode = list(df.select_dtypes(include=['category','object']))
        train = True;
    else:
        columnsToEncode = le_dict.keys()   
        train = False;

    for feature in columnsToEncode:
        if train:
            le_dict[feature] = LabelEncoder()
        try:
            if train:
                df[feature] = le_dict[feature].fit_transform(df[feature])
            else:
                df[feature] = le_dict[feature].transform(df[feature])

            df = pd.concat([df, 
                              pd.get_dummies(df[feature]).rename(columns=lambda x: feature + '_' + str(x))], axis=1)
            df = df.drop(feature, axis=1)
        except:
            print('Error encoding '+feature)
            #df[feature]  = df[feature].convert_objects(convert_numeric='force')
            df[feature]  = df[feature].apply(pd.to_numeric, errors='coerce')
    return (df, le_dict)

Это работает в кадре данных pandas и для каждого столбца созданный ими, и возвращает обратное отображение. Таким образом, вы бы назвали это следующим образом:

train_data, le_dict = oneHotEncode2(train_data)

Затем в тестовых данных вызов выполняется путем передачи словаря, возвращенного из обучения:

test_data, _ = oneHotEncode2(test_data, le_dict)

Эквивалентный метод заключается в использовании DictVectorizer. Связанная с ним публикация в том же разделе содержится в моем блоге. Я упоминаю ее здесь, поскольку она дает некоторые аргументы в пользу этого подхода, просто используя get_dummies post (раскрытие: это мой собственный блог).

2
задан Paul R 11 September 2013 в 15:28
поделиться

2 ответа

Я попробовал 3 компилятора: MS Visual Studio 2012, gcc481 и Intel icl 13.1. Все они предупреждают, когда вы указываете. Я обнаружил, что gcc и MS автоматически генерируют код инициализации для tmp1, даже когда они предупреждают об отсутствии инициализации. Компилятор MS генерирует нежелательный доступ к памяти: movaps xmm0,xmmword ptr [rsp]. Gcc генерирует более эффективный xorps xmm0,xmm0. Поэтому в случае gcc добавление tmp1=_mm_setzero_ps() устраняет предупреждение и создает точно такой же код, как и без него. В случае MS добавление tmp1=_mm_setzero_ps() делает код короче и, вероятно, быстрее. Только компилятор Intel достаточно умен, чтобы избежать ненужной инициализации. Ниже приведено возможное обходное решение для компиляторов MS и gcc:

    __m128 tmp1 = _mm_loadh_pi(_mm_load_ps (src), (__m64*)(src + 4));

Генерация кода:

movaps      xmm0,xmmword ptr [rcx]
movhps      xmm0,qword ptr [rcx+10h]

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

09/12/2013: тестовый код для различных идей подавления предупреждений:

#include <xmmintrin.h>
#include <stdint.h>
#include <stdio.h>

//---------------------------------------------------------------------------
// original code from http://download.intel.com/design/PentiumIII/sml/24504301.pdf
__m128 func1 (float *src)
    {
    __m128 tmp1;
    tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src)), (__m64*)(src+ 4));
    return tmp1;
    }

//---------------------------------------------------------------------------
// original code plus tmp1 initialization
__m128 func2 (float *src)
    {
    __m128 tmp1 = _mm_loadh_pi(_mm_loadl_pi (_mm_setzero_ps (), (__m64*)(src)), (__m64*)(src + 4));
    return tmp1;
    }

//---------------------------------------------------------------------------
// use redundant load to eliminate warning 
__m128 func3 (float *src)
    {
    __m128 tmp1 = _mm_loadh_pi(_mm_load_ps (src), (__m64*)(src + 4));
    return tmp1;
    }

//---------------------------------------------------------------------------

static void dump (void *data)
    {
    float *f16 = data;
    int index;

    for (index = 0; index < 4; index++)
        printf ("%g ", f16 [index]);
    printf ("\n");
    }

//---------------------------------------------------------------------------

int main (void)
    {
    float f [8] = {1, 2, 3, 4, 5, 6, 7, 8};
    __m128 tmp;

    tmp = func1 (f);
    dump (&tmp);
    tmp = func2 (f);
    dump (&tmp);
    tmp = func3 (f);
    dump (&tmp);
    return 0;
    }

команды сборки:

gcc  -O3 -Wall -Wfatal-errors sample.c -osample.exe
objdump -Mintel --disassemble sample.exe > disasm.txt

cl -Ox -Zi -W4 sample.c
dumpbin -disasm -symbols sample.exe > disasm.txt

icl -Ox -Zi sample.c                                           
dumpbin -disasm -symbols sample.exe > disasm.txt                  
0
ответ дан ScottD 18 August 2018 в 00:32
поделиться
  • 1
    Проклятый, я не проверял сгенерированный код с gcc, но просто попытался добавить инициализацию, и это немного замедлило код. Возможно, это всего лишь некоторый шум. Я буду повторять. gcc 4.8.0 mingw64 (сборка rubenvb) – galinette 12 September 2013 в 10:41
  • 2
    Я попытаюсь сравнить производительность load_ps. Также для этого требуется 16-байтовое выравнивание для src, тогда как loadl_pi - нет. Использование loadu_ps будет медленнее. – galinette 12 September 2013 в 10:43

Это то, что _mm_undefined_ps для (но это только на самом деле помогает коду с компилятором Intel. Другие компиляторы обычно относятся к нему аналогично _mm_setzero_ps).

И кроме того, вы хотите загрузить movsd два поплавка, которые нуль расширяют и ломают ложную зависимость от старого значения регистра, а не movlps, который сливается. (Если вы не строите жесткий старый 32-разрядный процессор с SSE1, но не SSE2, как и PIII, ваш код изначально был написан для.)

Чтобы перейти к double * и использовать _mm_load_sd. Вы не разыгрываете это самостоятельно, только через _mm_load_sd, поэтому я думаю, , это все еще 100-процентное строгое сглаживание. Тем не менее, это работает на современных компиляторах! Если это оказывается небезопасным, _mm_loadl_epi64 (movq) принимает аргумент __m128i const* (странно, потому что он загружает только младшие 64 бит, но это тип may_alias, который вы можете определенно использовать безопасно для чтения любого другого типа, например char*.)

static inline
__m128 stride_gather(float *src) {
    __m128 tmp1 = _mm_castpd_ps(_mm_load_sd((const double*)src));  // movsd
    tmp1 = _mm_loadh_pi(tmp1, (const __m64*)(src+4));              // movhps
    return tmp1;
}

gcc7 и позже использовать movq вместо movsd, что странно, но я думаю, что все в порядке. В худшем случае дополнительный цикл задержки задержки байпаса в качестве входа в movhps для некоторых старых процессоров, но не пропускная способность.

Другие 3 основных компилятора (clang / ICC / MSVC) все компилируют этот к ожидаемому movsd / movhps без ложной зависимости от старого значения xmm0. ( source + asm output в проводнике компилятора Godbolt .)

0
ответ дан Peter Cordes 18 August 2018 в 00:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: