การใช้ฟังก์ชั่น Python กับ Pandas จัดกลุ่ม DataFrame - วิธีที่มีประสิทธิภาพมากที่สุดในการคำนวณความเร็วคืออะไร?


9

ฉันกำลังจัดการกับ Pandas DataFrame ขนาดใหญ่ - ชุดข้อมูลของฉันคล้ายกับการdfตั้งค่าต่อไปนี้:

import pandas as pd
import numpy  as np

#--------------------------------------------- SIZING PARAMETERS :
R1 =                    20        # .repeat( repeats = R1 )
R2 =                    10        # .repeat( repeats = R2 )
R3 =                541680        # .repeat( repeats = [ R3, R4 ] )
R4 =                576720        # .repeat( repeats = [ R3, R4 ] )
T  =                 55920        # .tile( , T)
A1 = np.arange( 0, 2708400, 100 ) # ~ 20x re-used
A2 = np.arange( 0, 2883600, 100 ) # ~ 20x re-used

#--------------------------------------------- DataFrame GENERATION :
df = pd.DataFrame.from_dict(
         { 'measurement_id':        np.repeat( [0, 1], repeats = [ R3, R4 ] ), 
           'time':np.concatenate( [ np.repeat( A1,     repeats = R1 ),
                                    np.repeat( A2,     repeats = R1 ) ] ), 
           'group':        np.tile( np.repeat( [0, 1], repeats = R2 ), T ),
           'object':       np.tile( np.arange( 0, R1 ),                T )
           }
        )

#--------------------------------------------- DataFrame RE-PROCESSING :
df = pd.concat( [ df,
                  df                                                  \
                    .groupby( ['measurement_id', 'time', 'group'] )    \
                    .apply( lambda x: np.random.uniform( 0, 100, 10 ) ) \
                    .explode()                                           \
                    .astype( 'float' )                                    \
                    .to_frame( 'var' )                                     \
                    .reset_index( drop = True )
                  ], axis = 1
                )

หมายเหตุ:เพื่อจุดประสงค์ในการมีตัวอย่างน้อยที่สุดสามารถตั้งค่าได้อย่างง่ายดาย (ตัวอย่างเช่นdf.loc[df['time'] <= 400, :]) แต่เนื่องจากฉันจำลองข้อมูลต่อไปฉันคิดว่าขนาดดั้งเดิมจะให้ภาพรวมที่ดีขึ้น

สำหรับแต่ละกลุ่มที่กำหนดโดย['measurement_id', 'time', 'group']ฉันต้องเรียกใช้ฟังก์ชันต่อไปนี้:

from sklearn.cluster import SpectralClustering
from pandarallel     import pandarallel

def cluster( x, index ):
    if len( x ) >= 2:
        data = np.asarray( x )[:, np.newaxis]
        clustering = SpectralClustering( n_clusters   =  5,
                                         random_state = 42
                                         ).fit( data )
        return pd.Series( clustering.labels_ + 1, index = index )
    else:
        return pd.Series( np.nan, index = index )

เพื่อเพิ่มประสิทธิภาพฉันได้ลองสองวิธี:

แพคเกจ Pandarallel

วิธีแรกคือการขนานการคำนวณโดยใช้pandarallelแพคเกจ:

pandarallel.initialize( progress_bar = True )
df \
  .groupby( ['measurement_id', 'time', 'group'] ) \
  .parallel_apply( lambda x: cluster( x['var'], x['object'] ) )

อย่างไรก็ตามสิ่งนี้ดูเหมือนว่าจะเหมาะสมที่สุดเนื่องจากมันกินแรมจำนวนมากและไม่ได้ใช้คอร์ทั้งหมดในการคำนวณ (แม้จะระบุจำนวนของคอร์อย่างชัดเจนในpandarallel.initialize()เมธอด) นอกจากนี้บางครั้งการคำนวณก็สิ้นสุดลงด้วยข้อผิดพลาดต่าง ๆ ถึงแม้ว่าฉันจะไม่มีโอกาสค้นพบเหตุผลสำหรับสิ่งนั้น (อาจเป็นเพราะขาด RAM?)

UDF ของ PySpark Pandas

ฉันให้ Spark Pandas UDF ไปด้วยแม้ว่าฉันจะยังใหม่กับ Spark ก็ตาม นี่คือความพยายามของฉัน:

import findspark;  findspark.init()

from pyspark.sql           import SparkSession
from pyspark.conf          import SparkConf
from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types     import *

spark = SparkSession.builder.master( "local" ).appName( "test" ).config( conf = SparkConf() ).getOrCreate()
df = spark.createDataFrame( df )

@pandas_udf( StructType( [StructField( 'id', IntegerType(), True )] ), functionType = PandasUDFType.GROUPED_MAP )
def cluster( df ):
    if len( df['var'] ) >= 2:
        data = np.asarray( df['var'] )[:, np.newaxis]
        clustering = SpectralClustering( n_clusters   =  5,
                                         random_state = 42
                                         ).fit( data )
        return pd.DataFrame( clustering.labels_ + 1,
                             index = df['object']
                             )
    else:
        return pd.DataFrame( np.nan,
                             index = df['object']
                             )

res = df                                           \
        .groupBy( ['id_half', 'frame', 'team_id'] ) \
        .apply( cluster )                            \
        .toPandas()

น่าเสียดายที่ประสิทธิภาพไม่น่าพอใจเช่นกันและจากสิ่งที่ฉันอ่านในหัวข้อนี้อาจเป็นเพียงภาระของการใช้ฟังก์ชั่น UDF ที่เขียนใน Python และความต้องการที่เกี่ยวข้องในการแปลงวัตถุ Python ทั้งหมดให้เป็นวัตถุ Spark และย้อนกลับ

ดังนั้นนี่คือคำถามของฉัน:

  1. ฉันสามารถปรับวิธีใดวิธีหนึ่งเพื่อกำจัดปัญหาคอขวดที่เป็นไปได้และปรับปรุงประสิทธิภาพได้หรือไม่ (เช่นการตั้งค่า PySpark การปรับการทำงานย่อยให้เหมาะสมเป็นต้น)
  2. พวกเขามีทางเลือกที่ดีกว่านี้อีกไหม? พวกเขาเปรียบเทียบกับโซลูชั่นที่ให้มาในแง่ของประสิทธิภาพได้อย่างไร

2
คุณค้นคว้าเรื่องหมาดไหม?
Danila Ganchar

1
ยังไม่ได้ แต่ขอขอบคุณสำหรับคำแนะนำของคุณ - ฉันจะปล่อยมันไป
Kuba_

น่าเสียดายที่ฉันไม่ได้ทำงานกับdask(((ดังนั้นความคิดเห็นของฉันมันเป็นเพียงคำแนะนำสำหรับการวิจัย
Danila Ganchar

โดยการแสดงฉันหมายถึงเวลาที่การคำนวณเสร็จสมบูรณ์
Kuba_

คำตอบ:


1

ถาม : " สามารถปรับวิธีใดวิธีหนึ่งของฉันเพื่อกำจัดปัญหาคอขวดที่เป็นไปได้และปรับปรุงประสิทธิภาพหรือไม่(เช่นการตั้งค่า PySpark การปรับการดำเนินงานย่อยที่เหมาะสมเป็นต้น) "

+1สำหรับการกล่าวถึงค่า ใช้จ่ายในการตั้งค่าแอดออนสำหรับกลยุทธ์การคำนวณอย่างใดอย่างหนึ่ง สิ่งนี้ทำให้เกิดจุดคุ้มทุนเสมอหลังจากนั้น[SERIAL]กลยุทธ์ที่ไม่ใช่อาจบรรลุความสุขที่เป็นประโยชน์ของบางส่วนที่ต้องการ[TIME]-Domain speedup (แต่ถ้าอื่น ๆ โดยทั่วไปแล้ว - [SPACE]ค่าใช้จ่ายโดเมนอนุญาตหรือคงความเป็นไปได้ - ใช่ RAM .. การมีอยู่ของ & การเข้าถึงอุปกรณ์ขนาดงบประมาณและข้อ จำกัด อื่น ๆ ที่คล้ายกันในโลกแห่งความเป็นจริง)

อันดับแรก
การตรวจสอบก่อนการบินก่อนที่เราจะนำออก

สูตรใหม่ของค่าใช้จ่ายที่เข้มงวดของกฎหมาย Amdahlขณะนี้สามารถรวมpSO + pTOค่าใช้จ่ายทั้งสองค่าเหล่านี้และสะท้อนสิ่งเหล่านี้ในการทำนายระดับ Speedup ที่ทำได้รวมถึงจุดคุ้มทุน จุดซึ่งมันอาจจะมีความหมาย (ในราคา / ผลกระทบความรู้สึกประสิทธิภาพ) ไปขนาน

ป้อนคำอธิบายรูปภาพที่นี่

แต่
ที่เป็นไม่ได้ปัญหาหลักของเราที่นี่
สิ่งต่อไปนี้:

ถัดไป
เนื่องจากค่าใช้จ่ายในการคำนวณของSpectralClustering()ซึ่งจะไปที่นี่เพื่อใช้เคอร์เนล Radial Boltzmann ฟังก์ชั่น~ exp( -gamma * distance( data, data )**2 )ดูเหมือนว่าจะไม่มีล่วงหน้าจากการแยกของdata-object กว่าจำนวนของหน่วยงานแยกเป็น - distance( data, data )ส่วนประกอบโดยนิยามมี แต่ เยี่ยมชมdataองค์ประกอบทั้งหมด(อ้างอิงค่าใช้จ่ายในการสื่อสารของค่าใด ๆ ที่ส่งผ่านค่า - { process | node }ทอพอโลยีแบบกระจายได้ด้วยเหตุผลที่ชัดเจนว่าแย่มากหากไม่ใช่กรณีการใช้งานที่เลวร้ายที่สุดสำหรับ{ process | node }การประมวลผลแบบกระจายหากไม่ใช่รูปแบบการต่อต้านแบบตรง (ยกเว้นบางอย่างจริง ๆ ความจำหน่วยความจำน้อย / รัฐน้อย แต่ผ้าคอมพิวเตอร์)

สำหรับนักวิเคราะห์อวดความรู้ใช่ - เพิ่มนี้ (และเราอาจพูดไม่ดีรัฐ) ค่าใช้จ่ายของ - อีกครั้ง - ใด ๆ ต่อการใด ๆK-วิธี -processing นี่เกี่ยวกับการO( N^( 1 + 5 * 5 ) )ที่จะไปสำหรับN ~ len( data ) ~ 1.12E6+, ชะมัดกับความปรารถนาของเราที่จะมีบางส่วน การประมวลผลที่ชาญฉลาดและรวดเร็ว

แล้วอะไรล่ะ

ในขณะที่ค่าใช้จ่ายในการติดตั้งไม่ได้ละเลยที่เพิ่มขึ้นค่าใช้จ่ายในการสื่อสารเกือบจะปิดการใช้งานสำหรับการตรวจสอบการปรับปรุงใด ๆ จากการใช้ความพยายามในการข้างต้นร่างที่จะย้ายจาก pure- [SERIAL]กระบวนการไหลเข้ามาในรูปแบบของบางเพียง - [CONCURRENT]หรือ True- [PARALLEL]ประสานของบางส่วนทำงานหน่วยย่อย เนื่องจากค่าใช้จ่ายที่เพิ่มขึ้นที่เกี่ยวข้องกับการต้องในการดำเนินการ(คู่ตีคู่)ใด ๆ ต่อการใด ๆ โครงสร้างค่าผ่าน

ถ้าไม่ใช่เพราะ 'em?

นี่มันฟังดูเป็นวิทยาศาสตร์ในการคำนวณคอมพิวเตอร์ถึงแม้ว่ามันจะเป็นไปได้ก็ตามค่าใช้จ่ายของระยะทางใด ๆ ที่คำนวณล่วงหน้า (ซึ่งจะนำ[TIME]ค่าความซับซ้อนของโดเมนที่ใหญ่โต"มาก่อน" (ที่ไหน?) อื่น ๆ , ไม่สามารถหลีกเลี่ยงความหน่วงได้, อนุญาตให้แฝงความหน่วงแฝงที่เป็นไปได้โดยบางส่วน (ไม่ทราบจนถึง) การสะสมเพิ่มของเมทริกซ์ระยะทางใด ๆ ต่ออนาคตใด ๆ ที่สมบูรณ์ในอนาคต?)) แต่จะเปลี่ยนตำแหน่งต้นทุนเหล่านี้[TIME]- และ - [SPACE]โดเมนไม่ลด 'em

ถาม : "พวกเขามีทางเลือกที่ดีกว่านี้อีกไหม "

สิ่งเดียวที่ฉันรู้จนถึงตอนนี้คือการพยายามหากปัญหาเป็นไปได้ที่จะได้รับการกำหนดสูตรใหม่อีกครั้งหนึ่งปัญหาแฟชั่นที่มีสูตร QUBO (อ้างอิง: Q uantum- U nconstrained- B inary- O ptimisation ข่าวดีก็คือเครื่องมือในการทำเช่นนั้นมีฐานความรู้มือแรกและประสบการณ์การแก้ปัญหาในทางปฏิบัติที่มีอยู่และขยายใหญ่ขึ้น)

ถาม : พวกเขาเปรียบเทียบกับโซลูชันที่ให้มาในแง่ของประสิทธิภาพได้อย่างไร

ผลการดำเนินงานที่น่าทึ่ง - ปัญหาคิวคิวโอมีตัวแก้ปัญหาที่มีแนวโน้มO(1)(!) ในเวลาคงที่ (ใน[TIME]-Domain) และค่อนข้าง จำกัด ใน[SPACE]-Domain (ที่เทคนิคการประกาศ LLNL ล่าสุดอาจช่วยหลีกเลี่ยงโลกทางกายภาพการใช้ QPU ปัจจุบันข้อ จำกัด ของปัญหา ขนาด)


นี่เป็นคำตอบที่น่าสนใจ แต่ดูเหมือนว่าจะพลาดประเด็น - OP ฝึกฝนโมเดลขนาดเล็กหลาย ๆ ตัวไม่ใช่แบบเดียว ดังนั้นการสังเกตหลักของคุณส่วนใหญ่จะไม่เกี่ยวข้อง
user10938362

@ user10938362 สถานที่ให้บริการที่คุณอ้างสิทธิ์ (ฝึกอบรมโมเดลขนาดเล็ก ) แปลไปยังค่าใช้จ่ายในการประมวลผลขนาดใหญ่ที่โพสต์ไว้ข้างต้นได้อย่างไร ว่าหลายรุ่นที่มีขนาดเล็กสัญญารวมทฤษฎีการเจริญเติบโตเพียงเส้นตรงของ(ยังคง)ค่าใช้จ่ายใหญ่-O ของแต่ละบุคคล(ตอนนี้มีขนาดเล็กใน N, แต่ไม่ได้อยู่ในปัจจัยอื่น ๆ )การประมวลผลแต่คุณต้องเพิ่มนี้ผลรวมชะมัดราคาแพงมากขึ้นทุก ค่าใช้จ่ายเพิ่มเติมทั้งค่าติดตั้งและค่าโสหุ้ยการเลิกจ้างรวมทั้งต้นทุนส่วนเพิ่มสำหรับการสื่อสาร ( ค่าพารามิเตอร์ / ข้อมูล / ผลลัพธ์ + โดยทั่วไปยังคู่ของค่าใช้จ่ายในการประมวลผล SER / DES ในแต่ละขั้นตอน)
user3666197

0

นี่ไม่ใช่คำตอบ แต่ ...

ถ้าคุณวิ่ง

df.groupby(['measurement_id', 'time', 'group']).apply(
    lambda x: cluster(x['var'], x['object']))

(เช่นกับ Pandas เพียงอย่างเดียว) คุณจะสังเกตเห็นว่าคุณใช้หลายคอร์อยู่แล้ว นี่เป็นเพราะการsklearnใช้งานjoblibโดยค่าเริ่มต้นเพื่อขนานงาน คุณสามารถสลับตัวกำหนดตารางเวลาให้เป็นประโยชน์กับ Dask และอาจได้รับประสิทธิภาพมากกว่าการแชร์ข้อมูลระหว่างเธรด แต่ตราบใดที่งานที่คุณทำอยู่นั้นเป็น CPU-bound เช่นนี้จะไม่มีอะไรที่คุณสามารถทำได้เพื่อเร่งความเร็ว

กล่าวโดยย่อนี่คือปัญหาของอัลกอริทึม: ค้นหาสิ่งที่คุณต้องการคำนวณจริง ๆ ก่อนที่จะลองพิจารณาเฟรมเวิร์กต่าง ๆ สำหรับการคำนวณ


คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงพูดว่า "... การแบ่งปันข้อมูลระหว่างเธรด ... "เมื่อการแบ่งงานถูกจัดระเบียบโดยกระบวนการที่ถูกjoblibวางไข่ซึ่งไม่มีส่วนเกี่ยวข้องกับเธรด ขอบคุณสำหรับการชี้แจงข้อโต้แย้ง
user3666197

โดยปกติแล้ว jboblib จะใช้กระบวนการ แต่ก็สามารถใช้ dask เป็นแบ็กเอนด์ซึ่งคุณสามารถเลือกส่วนผสมของเธรดและกระบวนการ
mdurant

ฉันเป็นมือใหม่ในการคำนวณแบบขนาน แต่ถึงแม้ว่า sklearn จะใช้การขนานกันมันก็ไม่มีประโยชน์อะไรในการตั้งค่านี้ ฉันหมายถึงการดำเนินการโดย sklearn นั้นง่ายมากเนื่องจากการดำเนินการจัดกลุ่มแต่ละครั้งจะมีเพียง 10 คะแนน อีกครั้งฉันอาจจะผิดที่นี่ แต่ฉันคิดว่าวิธีที่เราทำการประมวลผลข้อมูลชิ้นขนานเป็นปัญหาที่แท้จริง
Kuba_

"มันไม่ได้ไร้ประโยชน์ในการตั้งค่านี้" - ดีคุณใช้ค่าประสิทธิภาพของ CPU 8 คอร์แทน 1
mdurant

0

ฉันไม่ใช่ผู้เชี่ยวชาญDaskแต่ให้รหัสต่อไปนี้เป็นพื้นฐาน:

import dask.dataframe as ddf

df = ddf.from_pandas(df, npartitions=4) # My PC has 4 cores

task = df.groupby(["measurement_id", "time", "group"]).apply(
    lambda x: cluster(x["var"], x["object"]),
    meta=pd.Series(np.nan, index=pd.Series([0, 1, 1, 1])),
)

res = task.compute()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.