Áp dụng hàm Python cho DataFrame được nhóm lại của Pandas - cách tiếp cận hiệu quả nhất để tăng tốc tính toán là gì?


9

Tôi đang xử lý Pandas DataFrame khá lớn - tập dữ liệu của tôi giống với dfthiết lập sau :

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
                )

Lưu ý: Với mục đích có một ví dụ tối thiểu, nó có thể dễ dàng được đặt lại (ví dụ với df.loc[df['time'] <= 400, :]), nhưng vì tôi mô phỏng dữ liệu dù sao tôi nghĩ rằng kích thước ban đầu sẽ cho một cái nhìn tổng quan tốt hơn.

Đối với mỗi nhóm được xác định bởi ['measurement_id', 'time', 'group']tôi cần gọi hàm sau:

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 )

Để tăng cường hiệu suất, tôi đã thử hai cách tiếp cận:

Gói Pandarallel

Cách tiếp cận đầu tiên là song song hóa các tính toán bằng pandarallelgói:

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

Tuy nhiên, điều này có vẻ không tối ưu vì nó tiêu tốn rất nhiều RAM và không phải tất cả các lõi đều được sử dụng trong tính toán (ngay cả khi chỉ định rõ ràng số lượng lõi trong pandarallel.initialize()phương thức). Ngoài ra, đôi khi các tính toán bị chấm dứt với các lỗi khác nhau, mặc dù tôi không có cơ hội tìm lý do cho điều đó (có thể là thiếu RAM?).

PandSpark Pandas UDF

Tôi cũng đã cho Spark Pandas UDF đi, mặc dù tôi hoàn toàn mới với Spark. Đây là nỗ lực của tôi:

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()

Thật không may, hiệu năng cũng không đạt yêu cầu và từ những gì tôi đọc được về chủ đề này, đây có thể chỉ là gánh nặng của việc sử dụng hàm UDF, được viết bằng Python và nhu cầu liên quan đến việc chuyển đổi tất cả các đối tượng Python sang các đối tượng Spark và ngược lại.

Vì vậy, đây là những câu hỏi của tôi:

  1. Một trong những cách tiếp cận của tôi có thể được điều chỉnh để loại bỏ các tắc nghẽn có thể và cải thiện hiệu suất? (ví dụ: thiết lập PySpark, điều chỉnh các hoạt động phụ tối ưu, v.v.)
  2. Họ có bất kỳ lựa chọn thay thế tốt hơn? Làm thế nào để họ so sánh với các giải pháp được cung cấp về hiệu suất?

2
bạn đã nghiên cứu dask ?
Danila Ganchar

1
Chưa, nhưng cảm ơn lời đề nghị của bạn - Tôi sẽ thực hiện
Kuba_

Thật không may, tôi đã không làm việc với dask((vì vậy nhận xét của tôi, đó chỉ là lời khuyên cho nghiên cứu.
Danila Ganchar

Theo hiệu suất, tôi có nghĩa là thời gian trong đó tính toán có thể được hoàn thành.
Kuba_

Câu trả lời:


1

Hỏi : " thể điều chỉnh một trong hai cách tiếp cận của tôi để loại bỏ các tắc nghẽn có thể và cải thiện hiệu suất không? (Ví dụ: thiết lập PySpark, điều chỉnh các hoạt động tối ưu phụ, v.v.) "

+1để đề cập đến chi phí bổ sung thiết lập cho cả chiến lược điện toán. Điều này luôn tạo ra một điểm hòa vốn, chỉ sau đó, một chiến lược phi chiến lược mới có thể đạt được bất kỳ niềm vui có lợi nào của một số tăng tốc tên miền mong muốn (tuy nhiên, nếu khác, thông thường, chi phí tên miền cho phép hoặc duy trì khả thi - có, RAM. .. sự tồn tại của & truy cập vào một thiết bị có kích thước như vậy, ngân sách và các ràng buộc trong thế giới thực tương tự khác)[SERIAL][TIME][SPACE]

Thứ nhất,
việc kiểm tra trước chuyến bay, trước khi chúng tôi cất cánh

Các mới, chi phí-nghiêm ngặt xây dựng Luật Amdahl hiện có thể kết hợp cả hai add-on pSO + pTOchi phí chung và phản ánh những trong việc dự đoán các thể đạt Speedup-mức bao gồm cả hòa vốn điểm, từ đó nó có thể trở nên có ý nghĩa (theo nghĩa chi phí / hiệu quả, ý nghĩa hiệu quả) để đi song song.

nhập mô tả hình ảnh ở đây

Tuy nhiên,
đó không phảivấn đề cốt lõi của chúng tôi ở đây .
Điều này đến tiếp theo:

Tiếp theo,
với các chi phí tính toán SpectralClustering(), sẽ sử dụng hạt nhân Hàm Radial Boltzmann ~ exp( -gamma * distance( data, data )**2 ), dường như không có sự tiến bộ nào từ việc phân chia data-object so với bất kỳ số lượng đơn vị công việc khác nhau, như distance( data, data )định nghĩa, theo định nghĩa, nhưng truy cập tất cả các phần tử data(tham khảo chi phí liên lạc của các { process | node }cấu trúc liên kết phân bổ giá trị chuyển sang bất kỳ giá trị nào , vì lý do rõ ràng, rất tệ nếu không phải là trường hợp sử dụng tồi tệ nhất để { process | node }xử lý phân phối, nếu không phải là mô hình chống thẳng (ngoại trừ một số loại vải thực sự phức tạp, ít bộ nhớ / trạng thái, nhưng tính toán).

Đối với các nhà phân tích phạm vi, có - thêm vào điều này (và chúng ta có thể đã nói là một trạng thái tồi tệ ) chi phí của - một lần nữa - bất kỳ phương tiện k-phương tiện nào , ở đây, về O( N^( 1 + 5 * 5 ) )điều đó N ~ len( data ) ~ 1.12E6+, hết sức chống lại mong muốn của chúng ta xử lý thông minh và nhanh chóng.

Vậy thì sao?

Trong khi chi phí thiết lập không được bỏ qua, các tăng chi phí truyền thông sẽ gần như chắc chắn vô hiệu hóa bất cứ cải tiến từ việc sử dụng những nỗ lực trên phác thảo để di chuyển từ một pure- [SERIAL]dòng chảy quá trình vào một số hình thức chỉ - [CONCURRENT]hoặc đúng- [PARALLEL]dàn nhạc của một số công việc-sub-đơn vị , do các chi phí gia tăng liên quan đến việc phải thực hiện (một cặp song song) bất kỳ cấu trúc liên kết giá trị chuyển tiếp nào.

Nếu nó không dành cho họ?

Chà, điều này nghe có vẻ như là một oxymoron Khoa học Máy tính - ngay cả khi có thể, chi phí của khoảng cách được tính toán trước (sẽ mất đi những [TIME]chi phí phức tạp-Tên miền lớn "trước đó" (Ở đâu? Làm thế nào? khác, độ trễ không thể tránh được, cho phép che giấu độ trễ có thể xảy ra bởi một số (chưa biết đến) sự tích tụ gia tăng của ma trận khoảng cách bất kỳ trong tương lai hoàn toàn trong tương lai?)) nhưng sẽ định vị lại các chi phí hiện tại này cho một số vị trí khác trong [TIME]- và [SPACE]-Domains, không giảm 'em.

Q : "Họ có bất kỳ lựa chọn thay thế tốt hơn? "

Điều duy nhất, tôi biết từ trước đến nay, là thử, nếu vấn đề có thể được tái lập thành một vấn đề khác, một công thức có vấn đề về QUBO (tham khảo: Q uantum- U nconstrained- B inary- O ptimisation , tin tốt là các công cụ để làm như vậy, một nền tảng kiến ​​thức trực tiếp và kinh nghiệm giải quyết vấn đề thực tế tồn tại và phát triển lớn hơn)

Q : Làm thế nào để họ so sánh với các giải pháp được cung cấp về hiệu suất?

Hiệu suất thật ngoạn mục - Vấn đề được xây dựng theo QUBO có một O(1)trình giải (!) [TIME]Đầy hứa hẹn trong thời gian không đổi (trong -Domain) và bị hạn chế phần nào trong [SPACE]-Domain (nơi các thủ thuật LLNL được công bố gần đây có thể giúp tránh thế giới vật lý này, triển khai QPU hiện tại, hạn chế vấn đề kích cỡ).


Đây là một câu trả lời thú vị, nhưng dường như bỏ lỡ điểm - OP đào tạo nhiều mô hình nhỏ, không phải là một mô hình duy nhất. Vì vậy, quan sát cốt lõi của bạn chủ yếu là không liên quan.
10938362

@ user10938362 Làm thế nào để tài sản được yêu cầu của bạn (đào tạo các mô hình nhỏ ) dịch sang một số khác ngoài số liệu chi phí xử lý lớn được đăng ở trên? Chắc chắn nhiều mô hình nhỏ hơn hứa hẹn một khoản tiền mặt lý thuyết phát triển chỉ tuyến tính của (vẫn) chi phí lớn-O của cá nhân (hiện nay nhỏ hơn trong N, nhưng không phải trong các yếu tố khác) xử lý, tuy nhiên, bạn cần phải thêm vào đó một số tiền hết sức đắt tiền hơn của tất cả các chi phí bổ sung của cả chi phí thiết lập và chi phí chấm dứt cộng với tất cả các chi phí liên lạc bổ sung (thông số / dữ liệu / kết quả + thường là các cặp chi phí xử lý SER / DES trong mỗi bước)
user3666197

0

Đây không phải là một câu trả lời, nhưng ...

Nếu bạn chạy

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

(nghĩa là chỉ với Pandas), bạn sẽ nhận thấy rằng bạn đã sử dụng một số lõi. Điều này là do sklearnsử dụng joblibtheo mặc định để song song công việc. Bạn có thể trao đổi bộ lập lịch có lợi cho Dask và có thể đạt được hiệu quả cao hơn khi chia sẻ dữ liệu giữa các luồng, nhưng miễn là công việc bạn đang làm bị ràng buộc bởi CPU như thế này, bạn sẽ không thể làm gì để tăng tốc.

Nói tóm lại, đây là một vấn đề thuật toán: tìm ra những gì bạn thực sự cần tính toán, trước khi cố gắng xem xét các khung khác nhau để tính toán nó.


Bạn có thể vui lòng giải thích tại sao bạn đề cập đến "... chia sẻ dữ liệu giữa các chủ đề ..." mỗi khi công-chia đã được tổ chức bởi joblib-spawned quá trình , mà không có gì để làm với chủ đề, càng ít với chia sẻ? Cảm ơn bạn đã làm rõ các loại tranh luận.
user3666197

Chính xác, jboblib thường sử dụng các quy trình, nhưng nó có thể thay thế sử dụng dask làm phụ trợ, nơi bạn có thể chọn hỗn hợp các luồng và quy trình.
mdurant

Tôi là người mới sử dụng máy tính song song, nhưng ngay cả khi sklearn sử dụng song song, nó không vô dụng trong cài đặt này? Ý tôi là, các thao tác được thực hiện bởi sklearn cực kỳ đơn giản vì mỗi thao tác phân cụm chỉ được áp dụng cho 10 điểm. Một lần nữa, tôi có thể sai ở đây, nhưng tôi nghĩ cách chúng ta xử lý song song các khối dữ liệu gốc là vấn đề thực sự.
Kuba_

"không phải nó vô dụng trong cài đặt này" - tốt, bạn sử dụng hiệu năng của 8 lõi CPU thay vì 1.
mdurant

0

Tôi không phải là một chuyên gia về Dask, nhưng tôi cung cấp mã sau đây làm cơ sở:

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.