1

インポートされた CSV ファイル (~500MB) から 4 つの列を抽出して、scikit-learn回帰モデルのフィッティングに使用しています。

抽出を行うために使用されるこの関数は非常に遅いようです。今日Pythonを学んだばかりですが、関数を高速化する方法について何か提案はありますか?

マルチスレッド/コアは使用できますか? 私のシステムには 4 つのコアがあります。

def splitData(jobs):
    salaries = [jobs[i]['salaryNormalized'] for i, v in enumerate(jobs)]
    descriptions = [jobs[i]['description'] + jobs[i]['normalizedLocation'] + jobs[i]['category'] for i, v in enumerate(jobs)]
    titles = [jobs[i]['title'] for i, v in enumerate(jobs)]

    return salaries, descriptions, titles

印刷タイプ(ジョブ)

<type 'list'>

印刷ジョブ[:1]

[{'category': 'Engineering Jobs', 'salaryRaw': '20000 - 30000/annum 20-30K', 'rawLocation': 'Dorking, Surrey, Surrey', 'description': 'Engineering Systems Analyst Dorking Surrey Salary ****K Our client is located in Dorking, Surrey and are looking for Engineering Systems Analyst our client provides specialist software development Keywords Mathematical Modelling, Risk Analysis, System Modelling, Optimisation, MISER, PIONEEER Engineering Systems Analyst Dorking Surrey Salary ****K', 'title': 'Engineering Systems Analyst', 'sourceName': 'cv-library.co.uk', 'company': 'Gregory Martin International', 'contractTime': 'permanent', 'normalizedLocation': 'Dorking', 'contractType': '', 'id': '12612628', 'salaryNormalized': '25000'}]


def loadData(filePath):
    reader = csv.reader( open(filePath) )
    rows = []

    for i, row in enumerate(reader):
        categories = ["id", "title", "description", "rawLocation", "normalizedLocation",
                        "contractType", "contractTime", "company", "category",
                        "salaryRaw", "salaryNormalized","sourceName"]

        # Skip header row
        if i != 0: 
            rows.append( dict(zip(categories, row)) )

    return rows



def splitData(jobs):
    salaries = []
    descriptions = []
    titles = []

    for i in xrange(len(jobs)):
        salaries.append( jobs[i]['salaryNormalized'] )
        descriptions.append( jobs[i]['description'] + jobs[i]['normalizedLocation'] + jobs[i]['category'] )
        titles.append( jobs[i]['title'] )

    return salaries, descriptions, titles



def fit(salaries, descriptions, titles):
    #Vectorize
    vect = TfidfVectorizer()
    vect2 = TfidfVectorizer()
    descriptions = vect.fit_transform(descriptions)
    titles = vect2.fit_transform(titles)

    #Fit
    X = hstack((descriptions, titles))
    y = [ np.log(float(salaries[i])) for i, v in enumerate(salaries) ]

    rr = Ridge(alpha=0.035)
    rr.fit(X, y)

    return vect, vect2, rr, X, y



jobs = loadData( paths['train_data_path'] )
salaries, descriptions, titles = splitData(jobs)
vect, vect2, rr, X_train, y_train = fit(salaries, descriptions, titles)
4

4 に答える 4

2

コードに複数の問題があり、パフォーマンスに直接影響を与えています。

  1. enumerateジョブ リストを複数回表示します。一度だけ列挙し、代わりに列挙リスト (変数に格納) を使用できます。
  2. 列挙された項目の値はまったく使用しません。必要なのはインデックスだけであり、組み込みrange関数を使用してこれを簡単に実現できます。
  3. 各リストは積極的に生成されます。何が起こるかは次のとおりです。最初のリストはプログラムの実行をブロックし、完了するまでに時間がかかります。計算がまったく同じである 2 番目と 3 番目のリストでも同じことが起こります。

私があなたに提案したいのは、ジェネレーターを使用して、データを怠惰な方法で処理することです。パフォーマンス効率が高く、外出先でデータを抽出できます。

def splitData(jobs):
    for job in jobs:
        yield job['salaryNormalized'], job['description'] + job['normalizedLocation'] + job['category'], job['title']
于 2013-05-01T20:16:03.893 に答える
1

間違っている場合は訂正してください。ただしTfidVectorizer、イテレータ (ジェネレータ式など) も受け入れるようです。これにより、このかなり大きなデータの複数のコピーがメモリに保持されるのを防ぐことができます。これがおそらく速度低下の原因です。または、ファイルを直接操作できることを確認してください。csvを個別のファイルに変換し、それらのファイルをTfidVectorizerメモリにまったく保持せずに直接フィードすることができます。

編集 1

もう少しコードを提供していただいたので、もう少し具体的に説明できます。

まず、loadData必要以上のことを行っていることに注意してください。に存在する機能を複製しますcsv.DictReader。それを使用する場合は、カテゴリ名のリストをスキップします。ファイルを開くための別の構文が使用されます。この方法では、ファイルが自動的に閉じられるためです。また、一部の名前は、より正確で Pythonic (アンダースコア スタイル) になるように変更されています。

def data_from_file(filename):
    rows = []
    with open(filename) as f:
        reader = csv.DictReader(f)
        for row in reader:
            rows.append(row)
    return rows

これを変更して、メモリ内のすべての行のリストを作成するのではなく、ファイルから読み取った直後に一度に 1 行ずつ返すようにします。これが魔法のように見える場合は、Python のジェネレーターについて少し読んでください。

def data_from_file(path):
    with open(filename) as f:
        reader = csv.DictReader(f)
        for row in reader:
            yield row

では、 を見てみましょうsplitData。次のようにもっときれいに書くことができます。

def split_data(jobs):
    salaries = []
    descriptions = []
    titles = []

    for job in jobs:
        salaries.append(job['salaryNormalized'] )
        descriptions.append(job['description'] + job['normalizedLocation'] + 
                            job['category'])
        titles.append(job['title'])

    return salaries, descriptions, titles

しかし、ここでも、メモリ内に 3 つの巨大なリストを作成したくありません。そして一般的に、この関数が 3 つの異なるものを与えることは実際的ではありません。したがって、それを分割するには:

def extract_salaries(jobs):
    for job in jobs:
        yield job['salaryNormalized']

等々。これは、ある種の処理パイプラインをセットアップするのに役立ちます。extract_salaries(data_from_file(filename))csv の 1 行から値を要求するたびに、読み取りとsalary抽出が行われます。次回は、2 番目のsalary. この単純なケースでは、関数を作成する必要はありません。代わりに、ジェネレータ式を使用できます。

salaries = (job['salaryNormalized'] for job in data_from_file(filename))
descriptions = (job['description'] + job['normalizedLocation'] +
                job['category'] for job in data_from_file(filename))
titles = (job['title'] for job in data_from_file(filename))

これらのジェネレーターをfitに渡すことができるようになりました。最も重要な変更は次のとおりです。

y = [np.log(float(salary)) for salary in salaries]

イテレータ (一度に 1 つの値を与えるもの) にインデックスを作成することはできないため、より多くの値がある限りsalaryfromを取得すると想定し、それを使用して何かを行います。salaries

最終的に、csv ファイル全体を複数回読み取ることになりますが、それがボトルネックになるとは思いません。それ以外の場合は、さらに再構築が必要です。

編集 2

使用DictReaderは少し遅いようです。理由はわかりませんが、独自の実装 (ジェネレーターに変更) に固執するか、namedtuples:を使用することをお勧めします。

def data_from_file(filename):
    with open(filename) as f:
        reader = csv.reader(f)
        header = reader.next()
        Job = namedtuple('Job', header)
        for row in reader:
            yield Job(*row)

次に、ドット ( job.salaryNormalized) で属性にアクセスします。とにかく、ファイルから列名のリストを取得できることに注意してください。コードで複製しないでください。

もちろん、最終的にファイルの単一のコピーをメモリに保持することを決定することもできます。その場合は、次のようにします。

data = list(data_from_file(filename))
salaries = (job['salaryNormalized'] for job in data)

機能はそのままです。への呼び出しlistは、ジェネレーター全体を消費し、すべての値を a に格納しますlist

于 2013-05-01T21:00:20.180 に答える
1

簡単なスピードアップの 1 つは、リストのトラバーサルを減らすことです。単一の辞書のタプルを返すジェネレーターまたはジェネレーター式を作成し、結果の iterable を圧縮できます。

(salaries, descriptions, titles) = zip(*((j['salaryNormalized'], j['description'] + j['normalizedLocation'] + j['category'], j['title']) for j in jobs))

残念なことに、それでも 3 つのかなり大きなメモリ内リストが作成されます。リスト内包表記ではなくジェネレータ式を使用すると、圧縮前に 3 要素タプルの完全なリストを作成することを少なくとも防ぐことができます。

于 2013-05-01T20:31:56.817 に答える
0

インデックスはまったく必要ありません。を使用するだけinです。これにより、余分なタプルのリストを作成する必要がなくなり、間接的なレベルが取り除かれます。

salaries = [j['salaryNormalized'] for j in jobs]
descriptions = [j['description'] + j['normalizedLocation'] + j['category'] for j in jobs]
titles = [j['title'] for j in jobs]

これでも、データを 3 回繰り返します。

あるいは、1 つのリスト内包表記ですべてを取得し、1 つのジョブからの関連データをタプルにグループ化することもできます。

data = [(j['salaryNormalized'], 
         j['description'] + j['normalizedLocation'] + j['category'],
         j['title']) for j in jobs]

最後に最善を尽くします。最初に辞書を作成するのではなく、CSV ファイルから直接リストを入力してみませんか?

import csv

with open('data.csv', 'r') as df:
    reader = csv.reader(df)
    # I made up the row indices...
    data = [(row[1], row[3]+row[7]+row[6], row[2]) for row in reader]
于 2013-05-01T20:24:20.270 に答える