O FeatureGenerator (FG) é um processo de transformação de dados que converte entradas brutas em features prontas para modelos. Ele garante a consistência entre a geração de amostras offline e online. Esse processo, também conhecido como transformação de features, modifica uma ou mais features por meio de diversos operadores disponíveis.
A geração de features concentra-se apenas nas transformações necessárias tanto para a geração de amostras offline quanto online. Se uma operação de transformação for necessária somente na etapa offline, não a defina como uma operação de FG. O diagrama a seguir mostra a posição do módulo FG na arquitetura de um sistema de recomendação.
O processo de geração de features consiste em uma série de operadores (operadores FG) executados em paralelo, seguindo a ordem topológica de um grafo acíclico direcionado (DAG) definido no arquivo de configuração.
Exemplo de arquivo de configuração
Configure os operadores de features na lista features. Cada operador deve incluir os parâmetros feature_name e feature_type. Para outros parâmetros de configuração, consulte Operadores de features integrados.
O parâmetro reserves especifica os campos repassados de uma tarefa offline. Esses campos são gerados na saída exatamente como foram recebidos, sem transformação.
{
"features": [
{
"feature_name": "goods_id",
"feature_type": "id_feature",
"value_type": "string",
"expression": "item:goods_id",
"default_value": "-1024",
"need_prefix": false
},
{
"feature_name": "color_pair",
"feature_type": "combo_feature",
"value_type": "string",
"expression": ["user:query_color", "item:color"],
"default_value": "",
"need_prefix": false
},
{
"feature_name": "current_price",
"feature_type": "raw_feature",
"value_type": "double",
"expression": "item:current_price",
"default_value": "0",
"need_prefix": false
},
{
"feature_name": "usr_cate1_clk_cnt_1d",
"feature_type": "lookup_feature",
"map": "user:usr_cate1_clk_cnt_1d",
"key": "item:cate1",
"need_discrete": false,
"need_key": false,
"default_value": "0",
"combiner": "max",
"need_prefix": false,
"value_type": "double"
},
{
"feature_name": "recommend_match",
"feature_type": "overlap_feature",
"method": "is_contain",
"query": "user:query_recommend",
"title": "item:recommend",
"default_value": "0"
},
{
"feature_name": "norm_title",
"feature_type": "text_normalizer",
"expression": "item:title",
"max_length": 512,
"parameter": 0,
"remove_space": false,
"is_gbk_input": false,
"is_gbk_output": false
},
{
"feature_name": "title_terms",
"feature_type": "tokenize_feature",
"expression": "feature:norm_title",
"default_value": "",
"vocab_file": "tokenizer.json",
"output_type": "word_id",
"output_delim": ","
},
{
"feature_name": "query_title_match_ratio",
"feature_type": "overlap_feature",
"method": "query_common_ratio",
"query": "user:query_terms",
"title": "feature:title_terms",
"default_value": "0"
},
{
"feature_name": "title_term_match_ratio",
"feature_type": "overlap_feature",
"method": "title_common_ratio",
"query": "user:query_terms",
"title": "feature:title_terms",
"default_value": "0"
},
{
"feature_name": "term_proximity_min_cover",
"feature_type": "overlap_feature",
"method": "proximity_min_cover",
"query": "user:query_terms",
"title": "feature:title_terms",
"default_value": "0"
}
],
"input_alias": {
"non_exist_field1": "exist_field1",
"non_exist_field2": "exist_field2"
},
"reserves": [
"request_id",
"user_id",
"is_click",
"is_pay",
"sample_weight",
"event_unix_time"
]
}
Item de configuração especial input_alias: dicionário que mapeia o nome de um campo de entrada potencialmente inexistente para um nome de campo real. A configuração input_alias tem suporte na versão 1.0.0 e posteriores. Geralmente, você pode ignorar essa configuração.
Caso de uso 1: definir um alias mais curto para um nome de campo longo.
Caso de uso 2: definir um alias para o segundo parâmetro quando um operador de feature personalizado usa a mesma entrada para dois parâmetros diferentes.
É possível reutilizar o mesmo campo de entrada em diferentes features, mas não dentro de uma única transformação. Configure input_alias para contornar essa restrição.
Por exemplo, se um operador de feature personalizado tiver dois parâmetros de entrada que exigem o mesmo campo
A, configure duas entradas,AeB, além de uminput_aliaspara mapear"B": "A". Durante a execução, os parâmetros do operador personalizado mudarão de (A, B) para (A, A).
Domínios de entrada
Um domínio de entrada indica a entidade da qual uma entrada se origina. Há suporte para os quatro tipos a seguir:
user: features do lado do usuário, incluindo perfis e estatísticas no nível do usuário.
context: features contextuais que mudam a cada solicitação, como hora, localização e clima.
item: features do lado do item, incluindo conteúdo estático e estatísticas no nível do item.
feature: saída de outro operador de feature.
O domínio de entrada feature é especial, pois configura dependências entre operadores. Coletivamente, todos os operadores formam um grafo acíclico direcionado (DAG). O framework executa essas transformações em paralelo, respeitando a ordem topológica. A topologia aparece na figura a seguir.
Por padrão, a saída dos nós intermediários em um DAG não serve como saída do FG. Use o parâmetro de configuração de feature stub_type para alterar esse comportamento.
Tipos multivalor e separadores
O FG aceita tipos de entrada complexos, como Array e Map, compatíveis com os tipos complexos do MaxCompute.
Features multivaloradas do tipo String podem usar chr(29) como delimitador.
Por exemplo, em v1^]v2^]v3, o caractere ^] atua como separador multivalor. Trata-se de um único caractere com código ASCII "\x1D", e não de dois caracteres separados. Para inserir esse caractere, pressione C-q C-5 no emacs ou C-v C-5 no vi.
Discretização de features (binning)
O framework oferece suporte aos seis tipos de operações de discretização a seguir:
hash_bucket_size: aplica hash ao resultado da transformação de feature e executa uma operação de módulo.
vocab_list: mapeia o resultado da transformação de feature para um índice em uma lista.
vocab_dict: mapeia o resultado da transformação de feature para um valor em um dicionário. O valor deve ser conversível para o tipo int64.
vocab_file: lê um
vocab_listouvocab_dictde um arquivo.boundaries: converte o resultado da transformação de feature em um ID de bucket correspondente, com base nos limites especificados.
num_buckets: usa o resultado da transformação de feature diretamente como o ID do bucket.
hash_bucket_size
Aplica hash ao resultado da transformação e executa uma operação de módulo. Este método aplica-se a qualquer tipo de valor de feature.
Intervalo do resultado: [0,
hash_bucket_size)Para valores vazios, o resultado da discretização é
hash(default_value)%hash_bucket_size.
{
"hash_bucket_size": 128000,
"default_value": "default_value"
}
vocab_list
Discretiza a entrada mapeando um valor de feature para seu índice correspondente no array vocab_list.
O tipo de elemento do array
vocab_listdeve corresponder à configuraçãovalue_type.-
num_oov_bucket: inteiro não negativo que define o número de buckets para termos fora do vocabulário.Todas as entradas fora do vocabulário receberão IDs no intervalo [vocabulary_size, vocabulary_size+num_oov_buckets) com base em um hash do valor de entrada.
Não especifique um num_oov_buckets positivo juntamente com
default_bucketize_value.
-
default_bucketize_value: valor inteiro de ID retornado para valores de feature fora do vocabulário.Não especifique este parâmetro quando
num_oov_bucketsfor positivo.O valor padrão é
vocab_list.size().
{
"vocab_list": [
"",
"<OOV>",
"token1",
"token2",
"token3",
"token4"
],
"num_oov_bucket": 0,
"default_bucketize_value": 1
}
vocab_dict
O resultado da discretização corresponde ao valor no dicionário vocab_dict associado à feature. Isso permite mapear diferentes valores de feature para o mesmo resultado de discretização.
O tipo de dados das chaves no dicionário
vocab_dictdeve corresponder à configuraçãovalue_type.Os valores de
vocab_dictdevem ser conversíveis para o tipoint64.-
num_oov_bucket: inteiro não negativo que define o número de buckets para termos fora do vocabulário.Todas as entradas fora do vocabulário receberão IDs no intervalo [vocabulary_size, vocabulary_size+num_oov_buckets) com base em um hash do valor de entrada.
Não especifique um num_oov_buckets positivo juntamente com
default_bucketize_value.
-
default_bucketize_value: valor inteiro de ID retornado para valores de feature fora do vocabulário.Não especifique este parâmetro junto com um
num_oov_bucketspositivo.O valor padrão é
vocab_dict.size().
{
"vocab_dict": {
"token1": 1,
"token2": 2,
"token3": 3,
"token4": 1
},
"num_oov_bucket": 0,
"default_bucketize_value": 4
}
vocab_file
Carrega um vocab_list ou vocab_dict de um arquivo.
{
"vocab_file": "vocab.txt",
"num_oov_bucket": 0,
"default_bucketize_value": 4
}
-
vocab_file: caminho para o arquivo de vocabulário. O arquivo contém um termo por linha. Opcionalmente, especifique um valor de mapeamento.Há suporte para caminhos relativos. Ao implantar o serviço online, coloque o arquivo no mesmo diretório do
fg.json.Se houver apenas um token, ele será mapeado para o número da linha (iniciando em 0). Se houver um valor associado, separe o token e o valor por um espaço em branco (espaço ou tabulação). O valor deve ser do tipo
int64.
num_oov_bucketedefault_bucketize_valuetêm o mesmo significado descrito anteriormente.
boundaries
Cria buckets para features numéricas com base nos limites de discretização especificados.
O tipo de elemento do array
boundariesdeve corresponder à configuraçãovalue_type.Os buckets incluem o limite esquerdo e excluem o limite direito.
Por exemplo,
boundaries=[0., 1., 2.]gera os buckets (-inf, 0.), [0., 1.), [1., 2.) e [2., +inf).
{
"boundaries": [0.0, 1.0, 2.0],
"default_value": -1
}
num_buckets
Usa o resultado da transformação de feature diretamente como o ID do bucket. Este método adequa-se a valores de feature conversíveis em inteiros.
Intervalo do resultado: [0,
num_buckets)Se o valor da feature estiver fora do intervalo configurado, ele receberá o valor
default_bucketize_value.
{
"num_buckets": 128000,
"default_bucketize_value": 127999
}
Operadores de features integrados
Os métodos de configuração variam entre os operadores de features. Todos os operadores que podem atuar como nós folha no DAG aceitam discretização.
Para obter mais informações, consulte Operadores de features integrados.
|
Tipo |
Descrição |
|
id_feature |
Feature categórica |
|
raw_feature |
Feature numérica |
|
expr_feature |
Feature de expressão |
|
combo_feature |
Feature de combinação |
|
combine_feature |
Feature de combinação (agregada em um único valor) |
|
lookup_feature |
Feature de busca em dicionário |
|
match_feature |
Feature de busca em dicionário com chave primária e secundária |
|
overlap_feature |
Feature de sobreposição |
|
sequence_feature |
Feature de sequência |
|
text_normalizer |
Normalização de texto |
|
tokenize_feature |
Feature de tokenização de texto |
|
bm25_feature |
Feature de relevância de texto BM25 |
|
kv_dot_product |
Produto escalar de vetor KV |
|
str_replace_feature |
Substituição de string |
|
regex_replace_feature |
Substituição por expressão regular |
|
slice_feature |
Fatiamento de array |
Combinações de operadores
Configure um DAG para combinar vários operadores integrados e realizar transformações de features avançadas.
Exemplo 1: média dos primeiros 4 elementos da sequência
{
"features": [
{
"feature_name": "top_n_prices",
"feature_type": "sequence_raw_feature",
"expression": "user:clk_prices",
"separator": ",",
"sequence_length": 4,
"stub_type": true
},
{
"feature_name": "top_n_avg_price",
"feature_type": "expr_feature",
"expression":"reduce_mean(top_n_prices)",
"default_value": "-1",
"variables":["feature:top_n_prices"]
}
]
}
Exemplo 2: média de elementos da sequência com condição
{
"features": [
{
"feature_name": "valid_list",
"feature_type": "expr_feature",
"expression":"clk_times < 10",
"variables":["user:clk_times"],
"value_dimension": 5
},
{
"feature_name": "top_n_prices",
"feature_type": "bool_mask_feature",
"expression": ["user:clk_prices", "feature:valid_list"],
"value_type": "float",
"separator": ","
},
{
"feature_name": "top_n_avg_price",
"feature_type": "expr_feature",
"expression":"reduce_mean(top_n_prices)",
"default_value": "-1",
"variables":["feature:top_n_prices"]
}
]
}
Nota: no exemplo anterior, clk_prices e clk_times são duas sequências paralelas.
Operadores de features personalizados
O framework carrega e executa dinamicamente operadores de features personalizados como plugins.
Para obter mais informações, consulte Operadores de features personalizados.
Otimização de desempenho
O desempenho do módulo FG depende fortemente da configuração. O princípio geral é minimizar transformações de dados (features) desnecessárias.
Processe e transforme dados nas etapas offline ou near-line sempre que possível. Evite executar essas operações na etapa de FG (serviço de pontuação online).
Siga estas diretrizes para melhorar o desempenho:
-
Para dados de entrada estruturados, priorize tipos complexos de tabelas MaxCompute (por exemplo, Map e Array) em vez do tipo STRING para reduzir a sobrecarga de análise de strings.
Em serviços de pontuação online (como EasyRec Processor ou TorchEasyRec Processor), use FeatureStore e FeatureDB como armazenamento online para habilitar o suporte a tipos complexos.
Para uma
lookup_feature, use preferencialmente o tipo Map no campomap.Para
sequence_feature,overlap_featureebm25_feature, use preferencialmente entradas do tipo Array.Evite
match_feature, pois não há suporte para tipos complexos. Em vez disso, uselookup_featurecombinandopkeyeskey.
-
Evite a sobrecarga causada pela conversão de tipos de dados.
Não defina o
value_typederaw_featurecomo um tipo diferente de float sem um motivo específico.Para uma
lookup_feature, garanta que o tipo da chave da entradaMap<Key, Value>corresponda ao tipo do campo de consulta.Ao configurar discretização do tipo
num_buckets, defina ovalue_typecomoint64.-
Se o tipo ideal para uma coluna de dados variar entre cenários, considere adicionar uma cópia da coluna com um tipo diferente.
Por exemplo, um campo precisa ser BIGINT quando usado como campo de busca para
lookup_feature, mas STRING quando faz parte de umacombo_feature.-
Nesse caso, adicione uma cópia da coluna para cada tipo necessário: uma BIGINT e uma STRING. Veja abaixo um exemplo de código SQL:
SELECT int_data, int_data as str_data FROM ....
Reutilize lógica e cálculos compartilhados sempre que possível, aproveitando as dependências de features (modo DAG).
Configuração global
|
Parâmetro |
Tipo |
Padrão |
Descrição |
|
USE_CITY_HASH_TO_BUCKETIZE |
string |
'false' |
Define se o CityHash será usado como função de hash para discretização de features. |
|
USE_MULTIPLICATIVE_HASH |
string |
'false' |
Define se o hash multiplicativo substituirá a operação de módulo para hashing de features. Esta opção é recomendada. |
|
DISABLE_FG_PRECISION |
string |
'true' |
Defina como 'false' para restringir features de ponto flutuante a seis casas decimais. O padrão é 'true', que desativa essa restrição. |
|
DISABLE_STRING_TRIM |
string |
'false' |
Define se a remoção de espaços à esquerda e à direita após a divisão de features de string multivaloradas será desativada. |
|
MONITOR_CUSTOM_OP_EVERY_N_SECONDS |
string |
'0' |
Especifica o intervalo, em segundos, para monitorar o desempenho de operadores personalizados e imprimir dados de desempenho. O valor '0' desativa o monitoramento. |
Nota: as configurações acima devem ser consistentes em todos os ambientes de execução, incluindo offline e online, bem como para treinamento e inferência. Caso contrário, podem ocorrer inconsistências entre a pontuação online e offline.
Taxa de colisão de hash
A tabela a seguir apresenta os resultados de testes em um conjunto de dados com 26 features de diferentes cardinalidades, onde o hash_bucket_size de cada feature foi definido como 10 × sua cardinalidade:
|
Tipo de hash |
Cardinalidade total de features |
Total de bins |
Taxa de colisão de hash |
|
std::hash |
882.774.549 |
840.065.238 |
4,8381% |
|
cityhash |
882.774.549 |
840.072.446 |
4,8373% |
|
std+cityhash |
882.774.549 |
840.075.948 |
4,8369% |
|
cityhash+multiplicative |
882.774.549 |
840.072.195 |
4,8373% |
|
std+multiplicative |
882.774.549 |
840.077.306 |
4,8367% |
Em resumo, recomendamos o uso combinado de std::hash + MultiplicativeHash para otimizar o desempenho do modelo. O std::hash vem ativado por padrão. Já o MultiplicativeHash permanece desativado por padrão para manter compatibilidade com versões anteriores; ative-o manualmente conforme as instruções abaixo.
Além disso, o CityHash é um método que teoricamente oferece melhor uniformidade, mas não demonstrou vantagem significativa neste conjunto de dados. Recomendamos testá-lo em seu próprio conjunto de dados.
Configuração do serviço de pontuação online
Defina essas configurações usando variáveis de ambiente no servidor. Especificamente, defina-as na configuração de serviço do EasyRec Processor ou do TorchEasyRec Processor.
{
"processor_envs": [
{
"name": "USE_MULTIPLICATIVE_HASH",
"value": "true"
}
]
}
Configuração de tarefas offline
Para executar tarefas offline de FG no ambiente MaxCompute, consulte Usar FG em tarefas offline.
Especificamente, consulte o código a seguir:
from pyfg105 import run_on_odps
fg_task = run_on_odps.FgTask(...)
fg_task.add_fg_setting('USE_CITY_HASH_TO_BUCKETIZE', 'false')
fg_task.add_fg_setting('USE_MULTIPLICATIVE_HASH', 'true')
fg_task.run(o)
Configuração da API pyfg
Ao utilizar a API pyfg, por exemplo para gerar features durante o treinamento, configure-a usando o método a seguir.
import pyfg
pyfg.set_env('USE_MULTIPLICATIVE_HASH', 'true')
pyfg.set_env('USE_CITY_HASH_TO_BUCKETIZE', 'false')