spark-Explanação detalhada das operações comuns de conversão e ação do rdd

Este artigo foi originado de uma conta pública pessoal: TechFlow , original não é fácil, procure atenção


Hoje é o terceiro artigo do Spark , continuamos analisando algumas operações do RDD.

Dissemos anteriormente que a operação do RDD no spark pode ser dividida em dois tipos, um é transformação e o outro é ação . Na operação de conversão, o spark não calculará os resultados para nós, mas gerará um novo nó RDD e gravará esta operação. Somente quando a operação de ação é executada, o spark calculará todo o cálculo desde o início.

As operações de conversão podem ser divididas em operações de conversão para elementos e operações de conversão para coleções.

Operações de conversão específicas de elementos

As operações de conversão para elementos são muito comuns e as mais usadas são map e flatmap. Do ponto de vista do nome, ambas são operações de mapa.Como todos sabemos sobre operações de mapa, elas são mencionadas nos artigos anteriores do MapReduce e nos artigos no mapa Python e reduzem o uso. Em resumo, uma operação pode ser mapeada para cada elemento.

Por exemplo, suponha que tenhamos uma sequência [1, 3, 4, 7] e queremos colocar cada elemento ao quadrado. Claro que podemos usar para execução de loop, mas a melhor maneira no spark é usar o mapa.

nums = sc.parallelize([1, 3, 4, 7])
spuare = nums.map(lambda x: x * x)

Sabemos que o mapa é uma operação de conversão; portanto, o square ainda é um RDD e o produzimos diretamente sem obter resultados, apenas informações relacionadas ao RDD:

O diagrama de conversão do RDD interno é semelhante a este:

Se quisermos ver os resultados, devemos executar operações de ação, como take , vamos dar uma olhada nos resultados:

Consistente com as nossas expectativas, a operação do mapa já deve ser familiar para os alunos que já prestaram atenção antes, então qual é esse mapa plano?

A diferença está neste flat, todos sabemos que flat significa flat, então flatmap significa que o resultado da execução do mapa é flat . Para ser franco, ou seja, se o resultado após a execução do mapa for uma matriz, a matriz será desmontada e o conteúdo interno será retirado e combinado.

Vamos dar uma olhada em um exemplo:

texts = sc.parallelize(['now test', 'spark rdd'])
split = texts.map(lambda x: x.split(' '))

Como o objeto que executamos map é uma string, uma matriz de strings será obtida após a operação de divisão de uma string . Se executarmos o mapa, o resultado será:

E se executarmos o flatmap? Também podemos tentar:

Em comparação, você notou a diferença?

Sim, o resultado da execução do mapa é uma matriz de matrizes, porque após cada divisão de cadeia de caracteres é uma matriz, emendar a matriz é naturalmente uma matriz de matrizes. O FlatMap achatará essas matrizes e as juntará, o que é a maior diferença entre as duas.

Ações de conversão para coleções

As operações de conversão para elementos estão descritas acima. Vamos dar uma olhada nas operações de conversão para coleções.

As operações para coleções são provavelmente união, distinção, interseção e subtração. Podemos primeiro olhar para a figura a seguir para ter uma sensação intuitiva e, em seguida, analisá-las uma a uma:

Primeiro, observe distinto, que, como o nome sugere, é remover duplicatas. É o mesmo que distinto no SQL. A entrada desta operação é dois conjuntos de RDDs. Após a execução, um novo RDD é gerado.Todos os elementos neste RDD são exclusivos . Uma coisa a observar é que o custo da execução distinta é muito grande, porque ele executará uma operação aleatória para desorganizar todos os dados e garantir que exista apenas uma cópia de cada elemento. Se você não entender o que significa a operação de reprodução aleatória, não importa, nos concentraremos em explicá-la nos artigos subseqüentes. Lembre-se de que é caro.

A segunda operação é a união, que também é fácil de entender, é mesclar todos os elementos nos dois RDDs . Você pode pensar nisso como uma operação de extensão em uma lista Python.Também é o mesmo que extensão.Ele não detecta elementos duplicados; portanto, se os mesmos elementos em dois conjuntos mesclados não forem filtrados, eles serão filtrados. Reservado.

A terceira operação é interseção, o que significa interseção , ou seja, a parte em que os dois conjuntos se sobrepõem. Isso deve ser bastante compreensível, vejamos a seguinte figura:

A parte azul na figura abaixo, que é a interseção dos dois conjuntos A e B, é o resultado de A.intersection (B), que é o elemento comum nos dois conjuntos. Da mesma forma, essa operação também executará a reprodução aleatória , portanto, a sobrecarga é igualmente grande e removerá elementos duplicados.

O último é o subtrair, que é o conjunto de diferenças , que é o elemento que pertence a A, mas não a B. Da mesma forma, podemos usar um gráfico para representar:

A parte sombreada em cinza na figura acima é a diferença entre os dois conjuntos A e B. Da mesma forma, esta operação também executará a reprodução aleatória, o que consome muito tempo.

Além do exposto, existem cartesianas, a saber, produto cartesiano , amostragem de amostras e outras operações de conjunto, mas relativamente pouco é usado, aqui não é introduzido muito, os estudantes interessados ​​podem aprender sobre isso, não é Complicado.

Operação de ação

A operação de ação mais usada no RDD deve ser a operação de obtenção do resultado, afinal calculamos por meio dia para obter o resultado. Obviamente, não é nosso objetivo obter o RDD. Os RDDs para obter resultados são principalmente pegar, cobrir e coletar.Esses três não têm uso especial.Deixe-me apresentá-los brevemente.

Onde collect é para obter todos os resultados, ele retornará todos os elementos. O take e o top precisam passar um parâmetro para especificar o número de itens. Take é retornar o número especificado de resultados do RDD. Top é retornar os primeiros resultados do RDD. O uso de top e take é exatamente o mesmo, a única diferença é Se o resultado obtido é o primeiro.

Além destes, há também uma ação muito comum é a contagem, isso não precisa ser dito, a operação de cálculo do número de dados, a contagem pode saber quantas partes de dados.

reduzir

Além desses relativamente simples, é interessante introduzir outros dois: primeiro, vamos introduzir reduzir. Reduzir, como o nome sugere, é reduzir no MapReduce e seu uso é quase o mesmo que reduzir no Python. Ele aceita uma função para executar a operação de mesclagem. Vejamos um exemplo:

Neste exemplo, nossa função de redução é adicionar duas entradas, e o mecanismo de redução repetirá essa operação para mesclar todos os dados; portanto, o resultado final é 1 + 3 + 4 + 7 = 15.

dobra

Além de reduzir, existe uma ação chamada fold. É exatamente o mesmo que reduzir. A única diferença é que ele pode personalizar um valor inicial e é para particionar. Também tomamos o exemplo acima como exemplo:

Olhar diretamente para este exemplo pode ser um pouco assustador.Uma explicação simples fará você entender, mas não é complicado. Percebemos que quando usamos o paralelismo para criar dados, adicionamos um parâmetro adicional 2, que representa o número de partições . Simples pode ser entendido como a matriz [1, 3, 4, 7] será dividida em duas partes, mas se coletarmos diretamente, ainda será o valor original.

Agora usamos fold, dois parâmetros são passados ​​e um valor inicial de 2 é passado além de uma função. Portanto, todo o processo de cálculo é assim:

A resposta para a primeira partição é 1 + 3 + 2 = 6, a resposta para a segunda partição é 4 + 7 + 2 = 13 e, finalmente, as duas partições são mescladas: 6 + 13 + 2 = 21.

Ou seja, atribuímos um valor inicial ao resultado de cada partição e atribuímos um valor inicial ao resultado após a mesclagem da partição.

agregar

Para ser sincera, essa ação é a mais difícil de entender porque é anormal. Primeiro, reduzir e dobrar exigem que o tipo do valor de retorno seja o mesmo que o tipo de dados rdd. Por exemplo, se o tipo de dados for int, o resultado retornado também deverá ser int.

Mas, em alguns cenários, isso não é aplicável, por exemplo, se queremos calcular a média, precisamos saber a soma do termo e o número de vezes que o termo aparece, portanto, precisamos retornar dois valores. Neste momento, o valor que inicializamos deve ser 0, 0, ou seja, para a soma e a contagem começam em 0, precisamos passar em duas funções, como escrever isto:

nums.aggregate((0, 0), lambda x, y: (x[0] + y, x[1] + 1), lambda x, y: (x[0] + y[0], x[1] + y[1]))

É inevitável ver essa linha de código sendo burra, não se preocupe, nós a explicamos pouco a pouco.

A primeira é a primeira função lambda, onde x não é um valor, mas dois valores , ou uma tupla de duas, que é o resultado que finalmente retornamos.Na nossa expectativa de retorno, o primeiro número retornado é Soma de nums, o segundo número retornado é o número de números em nums. Oy aqui é o resultado da entrada de nums. Obviamente, o resultado da entrada de nums é apenas um int, portanto, o y aqui é unidimensional. Então exigimos e, é claro, usamos x [0] + y, o que significa que o valor de y é adicionado à primeira dimensão e a segunda dimensão é naturalmente aumentada em um, porque devemos adicionar um toda vez que lermos um número.

Este ponto é relativamente fácil de entender.A segunda função pode ser um pouco trabalhosa.A segunda função é diferente da primeira.Não é usada para processar dados de números, mas para processar partições . Quando executamos agregados, o spark não é uma execução de thread único, ele dividirá os dados em nums em muitas partições, cada partição precisará ser mesclada após obter o resultado e essa função será chamada durante a mesclagem.

Semelhante à primeira função, o primeiro x é o resultado final e y é o valor que precisa ser mesclado no final de outras operações da partição. Portanto, y aqui é bidimensional, a primeira dimensão é a soma de uma determinada partição e a segunda dimensão é o número de elementos em uma determinada partição; então, é claro, temos que adicioná-la a x.

A figura acima mostra o processo de cálculo quando duas partições, em que lambda1 é a primeira função anônima pela qual passamos. Da mesma forma, lambda2 é a segunda função anônima pela qual passamos. Eu acho que deve ser fácil entender o gráfico de combinação.

Além disso, existem algumas operações de ação. Não as repetiremos por questão de espaço. Se houver alguma delas nos artigos subseqüentes, explicaremos em detalhes. Uma das principais razões pelas quais os iniciantes são mais resistentes à centelha de aprendizado é o fato de parecer muito complicado, e mesmo a operação ainda distingue entre operações de conversão e operações de ação. De fato, tudo isso é uma avaliação preguiçosa para otimizar o desempenho . Dessa forma, podemos combinar várias operações para executar em conjunto, reduzindo assim o consumo de recursos de computação.Para estruturas de computação distribuída, o desempenho é um indicador muito importante.Entendendo isso, por que o spark fez esse projeto? É fácil de entender.

Isso é verdade não apenas para o spark, mas também para estruturas de aprendizado profundo, como o TensorFlow. Em essência, muitos projetos aparentemente contra-intuitivos têm razões mais profundas. Após a compreensão, é realmente muito fácil de adivinhar. Operações de ação, se apenas alguns cálculos, oito em cada dez são operações de conversão.

Operação de resistência

Os RDDs no Spark são avaliados preguiçosamente e, às vezes, queremos usar o mesmo RDD várias vezes . Se simplesmente chamarmos a operação de ação, o spark calculará repetidamente o RDD e todos os seus dados correspondentes e outras dependências, o que obviamente gera muita sobrecarga. Naturalmente, esperamos que os RDDs que usamos frequentemente possam ser armazenados em cache e usados ​​a qualquer momento quando precisarmos, em vez de precisar executar novamente sempre que usá-los.

Para resolver esse problema, o spark fornece operações persistentes . A chamada persistência pode ser simplesmente entendida como cache. O uso também é muito simples, precisamos apenas persistir o RDD:

texts = sc.parallelize(['now test', 'hello world'])
split = texts.split(lambda x: x.split(' '))
split.persist()

Depois de chamar persistência, o RDD será armazenado em cache na memória ou no disco , e podemos chamá-lo a qualquer momento quando precisarmos, sem precisar executar todo o processo antes. E o spark suporta vários níveis de operações de persistência, podemos controlar através de variáveis StorageLevel . Vamos dar uma olhada no valor deste StorageLevel:

Podemos selecionar o nível de cache correspondente, conforme necessário. É claro que, como existe persistência, existe naturalmente anti-persistência.Para alguns RDDs que não precisam mais ser armazenados em cache, podemos chamar de não-persistente para removê-los do cache.

Embora o conteúdo de hoje pareça ser uma variedade de operações, algumas delas não são usadas com frequência. Precisamos apenas ter uma impressão. Os detalhes das operações específicas podem ser estudados com cuidado quando são usados. Espero que todos possam ignorar esses detalhes sem importância e compreender a essência do núcleo.

O artigo de hoje é exatamente isso: se você sente algo recompensado, siga ou repita seu trabalho.O seu esforço é muito importante para mim.

Insira a descrição da imagem aqui

Publicado 117 artigos originais · Gosto 61 · Visita 10.000+

Acho que você gosta

Origin blog.csdn.net/TechFlow/article/details/105622005
Recomendado
Clasificación