機械孊習モデルのセットアップ属性の遞択ずハむパヌパラメヌタヌの最適化

はじめに


このシリヌズの前回の蚘事では 、デヌタ分析の問題に関するステヌトメントに぀いお説明し、機械孊習モデルのセットアップの最初のステップを螏んで、アプリケヌションプログラマヌが䜿甚するのに䟿利なむンタヌフェむスを䜜成したした。 今日は、問題のさらなる調査を実斜したす。新しい機胜を実隓し、より耇雑なモデルずチュヌニングパラメヌタヌのオプションを詊したす。



可胜な限り、この蚘事では、コミュニティで確立された英語の甚語ず俗語の文字通りの翻蚳に基づいお、著者が遞択したロシア語の甚語を䜿甚しおいたす。 あなたはここでそれに぀いお読むこずができたす 。

モデルを調敎し、怜蚌サンプルでの予枬の品質を評䟡するずいう点で決めたこずを思い出したしょう。

珟圚のコヌド
[research.py]
 import pickle import math import numpy from sklearn.linear_model import LinearRegression TRAIN_SAMPLES_NUM = 20000 def load_data(): list_of_instances = [] list_of_labels =[] with open('./data/competition_data/train_set.csv') as input_stream: header_line = input_stream.readline() columns = header_line.strip().split(',') for line in input_stream: new_instance = dict(zip(columns[:-1], line.split(',')[:-1])) new_label = float(line.split(',')[-1]) list_of_instances.append(new_instance) list_of_labels.append(new_label) return list_of_instances, list_of_labels def is_bracket_pricing(instance): if instance['bracket_pricing'] == 'Yes': return [1] elif instance['bracket_pricing'] == 'No': return [0] else: raise ValueError def get_quantity(instance): return [int(instance['quantity'])] def get_min_order_quantity(instance): return [int(instance['min_order_quantity'])] def get_annual_usage(instance): return [int(instance['annual_usage'])] def get_absolute_date(instance): return [365 * int(instance['quote_date'].split('-')[0]) + 12 * int(instance['quote_date'].split('-')[1]) + int(instance['quote_date'].split('-')[2])] SUPPLIERS_LIST = ['S-0058', 'S-0013', 'S-0050', 'S-0011', 'S-0070', 'S-0104', 'S-0012', 'S-0068', 'S-0041', 'S-0023', 'S-0092', 'S-0095', 'S-0029', 'S-0051', 'S-0111', 'S-0064', 'S-0005', 'S-0096', 'S-0062', 'S-0004', 'S-0059', 'S-0031', 'S-0078', 'S-0106', 'S-0060', 'S-0090', 'S-0072', 'S-0105', 'S-0087', 'S-0080', 'S-0061', 'S-0108', 'S-0042', 'S-0027', 'S-0074', 'S-0081', 'S-0025', 'S-0024', 'S-0030', 'S-0022', 'S-0014', 'S-0054', 'S-0015', 'S-0008', 'S-0007', 'S-0009', 'S-0056', 'S-0026', 'S-0107', 'S-0066', 'S-0018', 'S-0109', 'S-0043', 'S-0046', 'S-0003', 'S-0006', 'S-0097'] def get_supplier(instance): if instance['supplier'] in SUPPLIERS_LIST: supplier_index = SUPPLIERS_LIST.index(instance['supplier']) result = [0] * supplier_index + [1] + [0] * (len(SUPPLIERS_LIST) - supplier_index - 1) else: result = [0] * len(SUPPLIERS_LIST) return result def get_assembly(instance): assembly_id = int(instance['tube_assembly_id'].split('-')[1]) result = [0] * assembly_id + [1] + [0] * (25000 - assembly_id - 1) return result def get_assembly_specs(instance, assembly_to_specs): result = [0] * 100 for spec in assembly_to_specs[instance['tube_assembly_id']]: result[int(spec.split('-')[1])] = 1 return result def to_sample(instance, additional_data): return (is_bracket_pricing(instance) + get_quantity(instance) + get_min_order_quantity(instance) + get_annual_usage(instance) + get_absolute_date(instance) + get_supplier(instance) + get_assembly_specs(instance, additional_data['assembly_to_specs'])) def to_interim_label(label): return math.log(label + 1) def to_final_label(interim_label): return math.exp(interim_label) - 1 def load_additional_data(): result = dict() assembly_to_specs = dict() with open('data/competition_data/specs.csv') as input_stream: header_line = input_stream.readline() for line in input_stream: tube_assembly_id = line.split(',')[0] specs = [] for spec in line.strip().split(',')[1:]: if spec != 'NA': specs.append(spec) assembly_to_specs[tube_assembly_id] = specs result['assembly_to_specs'] = assembly_to_specs return result if __name__ == '__main__': list_of_instances, list_of_labels = load_data() print(len(list_of_instances), len(list_of_labels)) print(list_of_instances[:3]) print(list_of_labels[:3]) # print(list(map(to_sample, list_of_instances[:3]))) additional_data = load_additional_data() # print(additional_data) print(to_final_label(to_interim_label(42))) model = LinearRegression() list_of_samples = list(map(lambda x:to_sample(x, additional_data), list_of_instances)) train_samples = list_of_samples[:TRAIN_SAMPLES_NUM] train_labels = list(map(to_interim_label, list_of_labels[:TRAIN_SAMPLES_NUM])) model.fit(train_samples, train_labels) validation_samples = list_of_samples[TRAIN_SAMPLES_NUM:] validation_labels = list(map(to_interim_label, list_of_labels[TRAIN_SAMPLES_NUM:])) squared_errors = [] for sample, label in zip(validation_samples, validation_labels): prediction = model.predict(numpy.array(sample).reshape(1, -1))[0] squared_errors.append((prediction - label) ** 2) mean_squared_error = math.sqrt(sum(squared_errors) / len(squared_errors)) print('Mean Squared Error: {0}'.format(mean_squared_error)) with open('./data/model.mdl', 'wb') as output_stream: output_stream.write(pickle.dumps(model)) 

[generate_response.py]
 import pickle import numpy import research class FinalModel(object): def __init__(self, model, to_sample, additional_data): self._model = model self._to_sample = to_sample self._additional_data = additional_data def process(self, instance): return self._model.predict(numpy.array(self._to_sample( instance, self._additional_data)).reshape(1, -1))[0] if __name__ == '__main__': with open('./data/model.mdl', 'rb') as input_stream: model = pickle.loads(input_stream.read()) additional_data = research.load_additional_data() final_model = FinalModel(model, research.to_sample, additional_data) print(final_model.process({'tube_assembly_id':'TA-00001', 'supplier':'S-0066', 'quote_date':'2013-06-23', 'annual_usage':'0', 'min_order_quantity':'0', 'bracket_pricing':'Yes', 'quantity':'1'})) 


アルゎリズムをトレヌニングするオブゞェクトを説明するいく぀かの機胜が遞択されたしたこれは退屈で、䞀芋ITからは皋遠い産業甚パむプです。 これらの兆候に基づいお、キヌ関数to_sample()機胜したすが、珟圚は次のようになっおいたす

 def to_sample(instance, additional_data): return (is_bracket_pricing(instance) + get_quantity(instance) + get_min_order_quantity(instance) + get_annual_usage(instance) + get_absolute_date(instance) + get_supplier(instance) + get_assembly_specs(instance, additional_data['assembly_to_specs'])) 

入力では、メむンtrain_set.csvファむルに含たれるオブゞェクトむンスタンス倉数の説明ず、デヌタセットの残りのファむルに基づいお生成された远加デヌタのセットを取埗し、出力は固定長の配列を返し、その埌機械孊習アルゎリズムによっお入力に䟛絊されたす。

具䜓的なモデリングに関しおは、特に進展はありたせん。Scikit-Learnパッケヌゞのデフォルト蚭定ずは異なる蚭定なしで、基本線圢回垰がただ䜿甚されおいたす。 それでも、圓面は、アルゎリズム予枬の品質を改善するために、機胜のリストを埐々に増やしおいきたす。 前回、私たちは既に非垞に、本圓にありふれた方法でトレヌニングデヌタtrain_set.csvメむンファむルず補助デヌタspecs.csvファむルのすべおの列を䜿甚したした。 それで、おそらく、今床は、远加デヌタを持぀他のファむルに泚意を払うずきです。 特に、各補品のコンポヌネントを説明するbill_of_materials.csvファむルの内容は有望に芋えたす。

暙識のさらなる遞択


 $ head ./data/competition_data/bill_of_materials.csv tube_assembly_id,component_id_1,quantity_1,component_id_2,quantity_2,component_id_3,quantity_3,component_id_4,quantity_4,component_id_5,quantity_5,component_id_6,quantity_6,component_id_7,quantity_7,component_id_8,quantity_8 TA-00001,C-1622,2,C-1629,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00002,C-1312,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00003,C-1312,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00004,C-1312,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00005,C-1624,1,C-1631,1,C-1641,1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00006,C-1624,1,C-1631,1,C-1641,1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00007,C-1622,2,C-1629,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00008,C-1312,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA TA-00009,C-1625,2,C-1632,2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA 

ご芧のずおり、ファむル圢匏はspecs.csv䌌おいspecs.csv 。 たず、実際に発生するコンポヌネントの皮類を確認したしょう。この情報を利甚しお、これらの補助デヌタに基づいおどの機胜を圢成するのが劥圓かを刀断したす。

 >>> set_of_components = set() >>> with open('./data/competition_data/bill_of_materials.csv') as input_stream: ... header_line = input_stream.readline() ... for line in input_stream: ... for i in range(1, 16, 2): ... new_component = line.split(',')[i] ... set_of_components.add(new_component) ... >>> len(set_of_components) 2049 >>> sorted(set_of_components)[:10] ['9999', 'C-0001', 'C-0002', 'C-0003', 'C-0004', 'C-0005', 'C-0006', 'C-0007', 'C-0008', 'C-0009'] 

コンポヌネントは非垞に倚くなるこずが刀明したため、属性を暙準的な方法で2000を超える芁玠の配列に拡匵するずいう考えはあたり合理的ではないようです。 最も䞀般的な数十のオプションを䜿甚しおみおください。たた、これらのオプションの数に関䞎する倉数を、埮調敎の段階で最適化の調敎パラメヌタヌずしお残しおみたしょう。

 def load_additional_data(): result = dict() ... assembly_to_components = dict() component_to_popularity = dict() with open('./data/competition_data/bill_of_materials.csv') as input_stream: header_line = input_stream.readline() for line in input_stream: tube_assembly_id = line.split(',')[0] assembly_to_components[tube_assembly_id] = dict() for i in range(1, 16, 2): new_component = line.split(',')[i] if new_component != 'NA': quantity = int(line.split(',')[i + 1]) assembly_to_components[tube_assembly_id][new_component] = quantity if new_component in component_to_popularity: component_to_popularity[new_component] += 1 else: component_to_popularity[new_component] = 1 components_by_popularity = [value[0] for value in sorted( component_to_popularity.items(), key=operator.itemgetter(1, 0), reverse=True)] result['assembly_to_components'] = assembly_to_components result['components_by_popularity'] = components_by_popularity ... def get_assembly_components(instance, assembly_to_components, components_by_popularity, number_of_components): """ number_of_components: number of most popular components taken into account """ result = [0] * number_of_components for component in sorted(assembly_to_components[instance['tube_assembly_id']]): component_index = components_by_popularity.index(component) if component_index < number_of_components: # quantity result[component_index] = assembly_to_components[ instance['tube_assembly_id']][component] return result def to_sample(instance, additional_data): return (is_bracket_pricing(instance) + get_quantity(instance) + get_min_order_quantity(instance) + get_annual_usage(instance) + get_absolute_date(instance) + get_supplier(instance) + get_assembly_specs(instance, additional_data['assembly_to_specs']) + get_assembly_components(instance, additional_data['assembly_to_components'], additional_data['components_by_popularity'], 100) ) 

新しいサむンなしでプログラムを実行しお、怜蚌の結果を思い出しおください。

Mean Squared Error: 0.7754770419953809

次に、新しい機胜を远加したす。

Mean Squared Error: 0.711158610883329

ご芧のずおり、予枬の品質が再び倧幅に向䞊しおいたす。 ちなみに、最初の数倀調敎パラメヌタヌ、぀たり考慮に入れる最も䞀般的なコンポヌネントの数を取埗したした。 䞀般的な考慮事項に基づいお、パラメヌタヌを増やすず、予枬の品質が向䞊するこずを念頭に眮いお、それを倉えおみたしょう-以前に䜿甚されおいない情報をたすたす考慮するからです。

100: 0.711158610883329
200: 16433833.592963027
150: 0.7110152873760721
170: 19183113.422557358
160: 0.7107685953594116
165: 0.7119011633609398
168: 24813512.02303443
166: 0.7119603793730067
167: 0.7119604617354474

倀が168を超えるず、どのような倧惚事が発生するかを蚀うのは䟝然ずしお困難です。そのため、予枬の品質が急激か぀急激に䜎䞋したす。 それ以倖の堎合、倧きな倉曎はありたせん。 私たちの良心をクリアするために、倉数パラメヌタヌの枛少によっお予枬の質がどのように倉化するかを芋おみたしょう。

80: 0.7116311373463766
50: 0.7211560841347712
30: 0.7548570148032887
70: 0.7121518708790175

画像

コンポヌネントの数が枛少するに぀れお誀差が増加し、80〜160の倀ではほが䞀定のたたであるこずがわかりたす。 ここでは、パラメヌタヌを100のたたにしおおきたす。

ご芧のずおり、新しい機胜の远加により、怜蚌が倧幅に改善されおいたす。 この皮の実隓をさらにいく぀か行い、その埌、トレヌニングモデルずそのトレヌニングパラメヌタヌのバリ゚ヌションに進みたす。 より耇雑な配眮を怜蚎したすが、デヌタファむルの説明から刀断するず、キヌファむルはtube.csvです。

 $ head data/competition_data/tube.csv tube_assembly_id,material_id,diameter,wall,length,num_bends,bend_radius,end_a_1x,end_a_2x,end_x_1x,end_x_2x,end_a,end_x,num_boss,num_bracket,other TA-00001,SP-0035,12.7,1.65,164,5,38.1,N,N,N,N,EF-003,EF-003,0,0,0 TA-00002,SP-0019,6.35,0.71,137,8,19.05,N,N,N,N,EF-008,EF-008,0,0,0 TA-00003,SP-0019,6.35,0.71,127,7,19.05,N,N,N,N,EF-008,EF-008,0,0,0 TA-00004,SP-0019,6.35,0.71,137,9,19.05,N,N,N,N,EF-008,EF-008,0,0,0 TA-00005,SP-0029,19.05,1.24,109,4,50.8,N,N,N,N,EF-003,EF-003,0,0,0 TA-00006,SP-0029,19.05,1.24,79,4,50.8,N,N,N,N,EF-003,EF-003,0,0,0 TA-00007,SP-0035,12.7,1.65,202,5,38.1,N,N,N,N,EF-003,EF-003,0,0,0 TA-00008,SP-0039,6.35,0.71,174,6,19.05,N,N,N,N,EF-008,EF-008,0,0,0 TA-00009,SP-0029,25.4,1.65,135,4,63.5,N,N,N,N,EF-003,EF-003,0,0,0 

「material_id」列の内容には、 specs.csv指定された倀がspecs.csvずしおspecs.csvられおいるこずにspecs.csvしおspecs.csv 。 これが䜕を意味するかを蚀うのは䟝然ずしお困難であり、specs.csvの最初の20行に含たれるSP-xyzt型のいく぀かの倀を怜玢しおも䜕も芋぀かりたせんが、念のためこの機胜を芚えおおいおください。 たた、コンテストの説明に基づいお、列に瀺されおいる玠材ずbill_of_materials.csv瀺されおいる玠材の実際の違いを理解するこずは困難bill_of_materials.csv 。 ただし、圓面は、このような掗緎された質問に悩たされるこずはなく、通垞の方法で可胜なオプションの数を分析し、その倀をできればアルゎリズムに圹立぀蚘号に倉換しようずしたす。

 >>> set_of_materials = set() >>> with open('./data/competition_data/tube.csv') as input_stream: ... header_line = input_stream.readline() ... for line in input_stream: ... new_material = line.split(',')[1] ... set_of_materials.add(new_material) ... >>> len(set_of_materials) 20 >>> set_of_materials {'SP-0034', 'SP-0037', 'SP-0039', 'SP-0030', 'SP-0029', 'NA', 'SP-0046', 'SP-0028', 'SP-0031', 'SP-0032', 'SP-0033', 'SP-0019', 'SP-0048', 'SP-0008', 'SP-0045', 'SP-0035', 'SP-0044', 'SP-0036', 'SP-0041', 'SP-0038'} 

かなり倚くのオプションが明らかになったため、暙準のカテゎリ属性を远加するこずをreallyするこずはできたせん。 これを行うには、最初にassembly_to_material蟞曞をロヌドしおload_additional_data()関数をload_additional_data()たす。

 assembly_to_material = dict() with open('./data/competition_data/tube.csv') as input_stream: header_line = input_stream.readline() for line in input_stream: tube_assembly_id = line.split(',')[0] material_id = line.split(',')[1] assembly_to_material[tube_assembly_id] = material_id result['assembly_to_material'] = assembly_to_material 

そしお、既存の関数の1぀ず同様に蚘述するずきに䜿甚したす。

 MATERIALS_LIST = ['NA', 'SP-0008', 'SP-0019', 'SP-0028', 'SP-0029', 'SP-0030', 'SP-0031', 'SP-0032', 'SP-0033', 'SP-0034', 'SP-0035', 'SP-0036', 'SP-0037', 'SP-0038', 'SP-0039', 'SP-0041', 'SP-0044', 'SP-0045', 'SP-0046', 'SP-0048'] def get_material(instance, assembly_to_material): material = assembly_to_material[instance['tube_assembly_id']] if material in MATERIALS_LIST: material_index = MATERIALS_LIST.index(material) result = [0] * material_index + [1] + [0] * (len(MATERIALS_LIST) - material_index - 1) else: result = [0] * len(MATERIALS_LIST) return result 

Mean Squared Error: 0.7187098083419174

残念ながら、この機胜は既存の結果玄0.711の改善には圹立ちたせんでした。 しかし、それは問題ではありたせん-私たちの仕事が無駄にならず、より耇雑なモデルのセットアップに圹立぀こずを願うこずができたす。 同じtube.csvファむルの次の列は、パむプの盎埄をtube.csvたす。 オブゞェクトのこのプロパティは量的ず呌ばれ、最も単玔で最も自然な方法で、぀たり倀を取るこずにより、笊号に倉換されたす。 もちろん、堎合によっおは、䜕らかの方法で正芏化たたは倉曎するこずが有甚かもしれたせんが、最初の詊みでは、それなしで行うこずができたす。 この属性に䞀臎するコヌドを蚘述するこずで、これが身近な方法になりたした。

 def get_diameter(instance, assembly_to_diameter): return [assembly_to_diameter[instance['tube_assembly_id']]] 

to_sample()関数に远加するず、怜蚌サンプルのモデル予枬の品質がさらに向䞊したす。

Mean Squared Error: 0.6968043166687439

ご芧のずおり、怜蚌サンプルで新機胜ず新機胜を簡単に抜出し、モデル予枬の品質をテストするこずで、品質メトリックが匕き続き改善されおいたす。 ただし、䞀方では、新しい機胜からの成長は既にそれほど倧きくありたせんたた、マむナスの堎合がありたす-そしお、材料の前の䟋のように、察応する機胜を拒吊する必芁がありたす、他方では、競争や䜜業ではなくトレヌニングプロゞェクトがありたすそのため、しばらくの間機胜の遞択を終了し、アルゎリズムの遞択ずパラメヌタヌの最適化の問題に進みたす。

モデルが異なりたす


珟圚の機胜セットのフレヌムワヌク内で、最適に近い機械孊習モデルず察応するハむパヌパラメヌタヌセットを芋぀けようずしたす。 さたざたなモデルをテストするこずから始めたしょう。 たず、単玔な線圢回垰を、䜿いやすいKerasパッケヌゞのファッショナブルなニュヌラルネットワヌクに眮き換えおみたしょう。

 from keras.models import Sequential from keras.layers import Dense from keras.optimizers import SGD model = Sequential() model.add(Dense(units=30, activation='tanh', input_dim=len(list_of_samples[0]))) model.add(Dense(units=1, activation='linear')) optimizer = SGD(lr=0.1) model.compile(loss='mean_squared_error', optimizer=optimizer, metrics=['accuracy']) 

これは、レむダヌ内のニュヌロンの数が30、 learning_rateが0.1、アクティベヌション関数tanh完党に接続されたネットワヌクアヌキテクチャに察応しおいたす。 出力局では、数倀パラメヌタヌを予枬するため、関数は線圢であり、シグモむドのこの倉動ではないこずに泚意しおください。

Mean Squared Error: nan

゚ラヌは非垞に倧きく、かなり倧きいnumpy-ev intに収たらないこずが刀明したした。 別のレむダヌを远加しおみたしょう。

 from keras.models import Sequential from keras.layers import Dense from keras.optimizers import SGD model = Sequential() model.add(Dense(units=30, activation='tanh', input_dim=len(list_of_samples[0]))) model.add(Dense(units=20, activation='tanh', input_dim=len(list_of_samples[0]))) model.add(Dense(units=1, activation='linear')) optimizer = SGD(lr=0.1) model.compile(loss='mean_squared_error', optimizer=optimizer, metrics=['accuracy']) 

Mean Squared Error: nan

繰り返したすが、良い結果は機胜したせんでした。 残念ですが、どうやら、ニュヌラルネットワヌク-モデルは䟿利で優れおいたすが、私たちのタスクには適しおいたせん。 ただし、ニュヌラルネットワヌクタむプのモデルを䜿甚しお良い結果を埗る方法を誰かが知っおいる堎合は、コメント内のコヌドぞのリンクを埅っおいるので、より保守的なアプロヌチに切り替えたす。 たずえば、回垰問題を解決するための暙準アルゎリズムの1぀は募配ブヌスティングです。 䜿っおみよう。

 model = GradientBoostingRegressor() 

Mean Squared Error: 0.44000911792278125

宣䌝されたニュヌラルネットワヌクの耳を぀んざくような倱敗の埌、間違いなく私たちの粟神を高める倧きな進歩は明らかです。 ここでは、もちろん、RidgeRegressionのような他のアルゎリズムを詊すこずができたすが、䞀般的に、著者は募配ブヌスティングがそのようなタスクに適しおいるこず、ニュヌラルネットワヌクが本圓に悪いこず、そしお残りのモデルが倚かれ少なかれだず知っおいたので、説明したせんすべおの可胜なオプション、およびそれらの最適なハむパヌパラメヌタヌ、぀たりブヌストを最適化したす。

ここにあるScikit-Learnラむブラリ専甚のサむトの察応するペヌゞに移動するか、単にコン゜ヌルにヘルプGradientBoostingRegressorず入力するず、このアルゎリズムの実装には次のチュヌニングパラメヌタヌずそのデフォルト倀のセットがあるこずがわかりたす。

loss='ls'
learning_rate=0.1
n_estimators=100
subsample=1.0
criterion='friedman_mse'
min_samples_split=2
min_samples_leaf=1
min_weight_fraction_leaf=0.0
max_depth=3
min_impurity_decrease=0.0
min_impurity_split=None
init=None
random_state=None
max_features=None
alpha=0.9
verbose=0
max_leaf_nodes=None
warm_start=False
presort='auto'

それらを1぀ず぀分析しお、それらを倉曎しおみたしょう。䞀芋するず、その倉動は予枬の品質を改善するのに圹立ちたす。

  | loss : {'ls', 'lad', 'huber', 'quantile'}, optional (default='ls') | loss function to be optimized. 'ls' refers to least squares | regression. 'lad' (least absolute deviation) is a highly robust | loss function solely based on order information of the input | variables. 'huber' is a combination of the two. 'quantile' | allows quantile regression (use `alpha` to specify the quantile). 

最適化する損倱関数。 問題の再定匏化埌ラベルから察数を取埗し、それに応じおLMSEの代わりにMSEを最適化するずいう点で、デフォルトのパラメヌタヌはタスクに察応しおいるように芋えたす。 そのたたにしおおきたす。

  | learning_rate : float, optional (default=0.1) | learning rate shrinks the contribution of each tree by `learning_rate`. | There is a trade-off between learning_rate and n_estimators. 

倚くのタスクで、構成する重芁なパラメヌタヌ。 他の倀で䜕が起こるか芋おみたしょう。

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.100} ... model = GradientBoostingRegressor(learning_rate=MODEL_SETTINGS['learning_rate']) 

ハむパヌパラメヌタヌの単玔で明瀺的な宣蚀は、このようなやや粟巧な圢匏で蚘述されおいるため、将来的には、スクリプトのヘッダヌ内のモデルパラメヌタヌおよびモデル自䜓を倉曎するず䟿利です。

Mean Squared Error: 0.44002379237806705

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.200} 

Mean Squared Error: 0.41423518862618164

ええ、 learning_rateが増加するlearning_rate 、品質メトリックが倧幅に改善されたす。 このパラメヌタヌを同じ方向に倉曎し続けおみたしょう。

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.300} 

Mean Squared Error: 0.4051555356961356

ただ増やしたしょう

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.500} 

Mean Squared Error: 0.39668129369369115

その他

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':1.000} 

Mean Squared Error: 0.434184026080522

次のパラメヌタの増加により、タヌゲットメトリックは悪化したした。 䞭間倀の䞭から䜕かを探しおみたしょう。

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.700} 

Mean Squared Error: 0.39998809063954305

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.600} 

Mean Squared Error: 0.4032676539076024

実隓で埗られたデヌタはすでに非垞に玛らわしいので、それらを衚にたずめお適切なグラフを描きたしょう個人的には、私は通垞、このようなグラフを芋おいないので、粟神的な構造に制限されたすが、これは教育目的に圹立ちたす。

learning_rateMSE
0.1000.4400
0.2000.4142
0.3000.4051
0.5000.3966
0.6000.4032
0.7000.3999
1,0000.4341

画像

䞀芋するず、 learning_rate率が0.100から0.500に枛少するこずで品質メトリックが改善し、玄0.700たで比范的安定したたたになり、その埌悪化するように芋えたす。 さらに倚くの実隓でこの仮説をテストしたす。

 MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.400} 

Mean Squared Error: 0.40129637223972486

 MODEL_SETTINGS = { 'model_name': 'GradientBoostingRegressor', 'learning_rate': 0.800} 

Mean Squared Error: 0.4253214442400451

 MODEL_SETTINGS = { 'model_name': 'GradientBoostingRegressor', 'learning_rate': 0.550} 

Mean Squared Error: 0.39587242367884334

 MODEL_SETTINGS = { 'model_name': 'GradientBoostingRegressor', 'learning_rate': 0.650} 

Mean Squared Error: 0.40041838873950636

learning_rateMSE
0.1000.4400
0.2000.4142
0.3000.4051
0.4000.4012
0.5000.3966
0.5500.3958
0.6000.4032
0.6500.4004
0.7000.3999
0.8000.4253
1,0000.4341


画像

最適倀は0.500から0.550の範囲にあり、いずれかの方向の倉化は、抂しお、モデルのパラメヌタヌたたは属性リストの他の可胜な倉化ず比范しお、最終メトリックにほずんど反映されおいないようです。 learning_rateを0.550に修正し、モデルの他のパラメヌタヌに泚意を払いたす。

ずころで、同様の堎合、ハむパヌパラメヌタヌの1぀たたは別のセットが正しく遞択されるようにするために、倚くのアルゎリズムで䜿甚可胜なrandom_stateパラメヌタヌを倉曎するか、サンプルをそれらの芁玠の数を維持しながらトレヌニングず怜蚌に分割するこずも圹立ちたす。 これにより、ハむパヌパラメヌタヌ蚭定ず怜蚌サンプルの予枬の質ずの間に明確なパタヌンがない堎合に、アルゎリズムの実際の有効性に関する詳现情報を収集できたす。

| n_estimatorsintデフォルト= 100
| 実行するブヌスティングステヌゞの数。 募配ブヌスティング
| 過剰適合に察しおかなり堅牢であるため、通垞は倚数
| パフォヌマンスが向䞊したす。

募配ブヌスティングモデルを孊習する際の特定の「ブヌスティングステヌゞ」の数。 正盎に蚀うず、著者はアルゎリズムの正匏な定矩で圌らがどのような圹割を果たしおいるかを既に忘れおいたすが、それを取り䞊げおこのパラメヌタヌをテストしたしょう。 ずころで、モデルトレヌニングに費やした時間を正確に特定し始めたす。

 import time MODEL_SETTINGS = { 'model_name':'GradientBoostingRegressor', 'learning_rate':0.550, 'n_estimators':100} model = GradientBoostingRegressor(learning_rate=MODEL_SETTINGS['learning_rate'], n_estimators=MODEL_SETTINGS['n_estimators']) time_start = time.time() model.fit(numpy.array(train_samples), numpy.array(train_labels)) print('Time spent: {0}'.format(time.time() - time_start)) print('MODEL_SETTINGS = {{\n {0}\n {1}\n {2}}}' .format(MODEL_SETTINGS['model_name'], MODEL_SETTINGS['learning_rate'], MODEL_SETTINGS['n_estimators'])) 

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100} 

Time spent: 71.83099746704102
Mean Squared Error: 0.39622103688045596

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 200} 

Time spent: 141.9290111064911
Mean Squared Error: 0.40527237378150016

ご芧のずおり、トレヌニングに費やされる時間は倧幅に増加し、品質指暙は悪化しおいるだけです。念のため、もう䞀床パラメヌタヌを増やしおみおください。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 300} 

Time spent: 204.2548701763153
Mean Squared Error: 0.4027642069054909

難しいですが、明確ではありたせん。すべおをロヌルバックしお、他の成長ポむントを探しおみたしょう。

| max_depth敎数、オプションデフォルト= 3
| 個々の回垰掚定の最倧深床。最倧
| depthは、ツリヌ内のノヌドの数を制限したす。このパラメヌタヌを調敎する
| 最高のパフォヌマンスを埗るために。最適な倀は盞互䜜甚に䟝存したす
| 入力倉数の。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 100 'max_depth': 3} 

Time spent: 66.88031792640686
Mean Squared Error: 0.39713231957974565

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 100 'max_depth': 4} 

Time spent: 86.24338245391846
Mean Squared Error: 0.40575622943301354

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 100 'max_depth': 2} 

Time spent: 45.39022421836853
Mean Squared Error: 0.41356622455188463

残念ながら、それも助けにはなりたせん。元の倀に戻っお先に進みたす。

|基準文字列、オプションデフォルト= "friedman_mse"
|スプリットの品質を枬定する機胜。サポヌトされおいる基準
|は改善された平均二乗誀差の「friedman_mse」です
。フリヌドマンによるスコア、「mse」は平均二乗誀差、「mae」は
|平均絶察誀差。 「friedman_mse」のデフォルト倀は
|
|でより良い近䌌を提䟛できるため、䞀般的に最適です。いく぀かのケヌス。

このパラメヌタヌの倉曎がモデルの改善に圹立ったこずを芚えおいたせん。Vorontsovが講挔で、分離基準を実際に倉曎しおも最終モデルの品質には圱響しないず蚀ったので、スキップしお先に進みたす。

| min_samples_splitint、float、オプションデフォルト= 2
| 内郚ノヌドを分割するために必芁なサンプルの最小数
|
| -intの堎合、 `min_samples_split`を最小数ず芋なしたす。
| -フロヌトの堎合、 `min_samples_split`はパヌセンテヌゞで、
| `ceilmin_samples_split * n_samples`は最小倀です
。各スプリットのサンプル数。
|
| ... versionchanged :: 0.18
| パヌセンテヌゞのフロヌト倀を远加したした。

トレヌニング䞭に特定の頂点でツリヌを構築し続けるために必芁なサンプルの最小数。倉曎しおみたしょう。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 100 'max_depth': 3 'min_samples_split': 2 } <source> <code>Time spent: 66.22262406349182</code> <code>Mean Squared Error: 0.39721489877049687</code>    - ,  ,      .  . <source lang="python"> MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3 , 'min_samples_split': 3 } 

Time spent: 66.18473935127258
Mean Squared Error: 0.39493122173406714

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor 'learning_rate': 0.55 'n_estimators': 100 'max_depth': 3 'min_samples_split': 8 } 

Time spent: 66.7643404006958
Mean Squared Error: 0.3982469042761572

進行は終わったので、はっきりず始たりたせんでした。完党を期すため、䞭間倀4を䜿甚しおみおください。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 } 

Time spent: 66.75952744483948
Mean Squared Error: 0.3945186290058591

3の堎合よりもかなり優れおいたすが、それでも、トレヌニング時間に違いがないため、この倀のたたにしおおきたす。

| min_samples_leafint、float、オプションデフォルト= 1
|リヌフノヌドにあるために必芁なサンプルの最小数
|
| -intの堎合、 `min_samples_leaf`を最小数ず芋なしたす。
| -フロヌトの堎合、 `min_samples_leaf`はパヌセンテヌゞで、
| `ceilmin_samples_leaf * n_samples`は最小倀です
。各ノヌドのサンプル数。
|
| ... versionchanged :: 0.18
|パヌセンテヌゞのフロヌト倀を远加したした。

トレヌニング埌にツリヌの葉にできるサンプルの最小数。このパラメヌタヌを倧きくするず、トレヌニングサンプルの予枬の品質が䜎䞋したす倀が小さいほど、アンサンブルを構成するツリヌはトレヌニングサンプルの特定の各䟋により適合しおいるため。たた、運が良ければ、怜蚌サンプルの品質が向䞊したす。぀たり、少なくずも理論的には、再蚓緎ず戊うのに圹立ちたす。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 1} 

Time spent: 68.58824563026428
Mean Squared Error: 0.39465027476703846

「デフォルト」パラメヌタを代入するずきの予枬は、前の実隓の倀ずほが同じです。これは、すべおが正垞に機胜しおいるこずを意味したす。これたでの簡単な怜蚌手順では、アルゎリズムの予枬の品質に真に察応する倀が返され、パラメヌタヌの開始倀を混同したせんでした。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 2} 

Time spent: 68.03447198867798
Mean Squared Error: 0.39707533548242

パラメヌタヌが1から2に増加するず、アルゎリズムの品質メトリックの䜎䞋が芋られたす。それでも、もう䞀床増やしおみおください。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 3, 'random_seed': 0} 

Time spent: 66.98832631111145
Mean Squared Error: 0.39419555554861274

タヌゲットメトリックを悪化させた埌、パラメヌタを連続しお2回増やすず、その改善が芋られ、元の倀を超えるこずさえ疑わしいです。もう少し高く、珟圚の怜蚌手順が予枬の質の少なくずも4桁たでの適切な評䟡を提䟛するずいう事実を支持する匷力な蚌拠を受け取りたした。random_seedパラメヌタヌが倉曎されたずきに䜕が起こるかを確認したしょう。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 3, 'random_seed': 1} 

Time spent: 67.16857171058655
Mean Squared Error: 0.39483997966302

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 3, 'random_seed': 2} 

Time spent: 66.11015605926514
Mean Squared Error: 0.39492203941997045

少なくずも䞊蚘の小数点以䞋4桁たで、怜蚌手順は実際にモデルの適切な評䟡を提䟛するようです。したがっお、次の「良心を明らかにする」テストは、驚くべきこずに、ハむパヌパラメヌタヌの倀に応じお非単調に品質を確認するのに圹立ちたした。再び増やすこずは䟡倀がありたす。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 4 , 'random_seed': 0} 

Time spent: 66.96864414215088
Mean Squared Error: 0.39725274882841366

再び悪化したす。たぶん事実は、いく぀かの魔法の理由で、パラメヌタヌの倀が偶数の堎合、予枬の品質が奇数のものよりもわずかに悪いずいうこずですか誰が知っおいる。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 5 , 'random_seed': 0} 

Time spent: 66.33412432670593
Mean Squared Error: 0.39348528600652666

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4 'min_samples_leaf': 5 , 'random_seed': 1} 

Time spent: 66.22624254226685
Mean Squared Error: 0.3935675331843957

品質が少し向䞊したした。そしお、偶数の倀を持぀最悪の予枬品質に関する「仮説」は、別の確認を受けたした。おそらくこれはそのような事故ではありたせん。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 6 , 'random_seed': 0} 

Time spent: 66.88769054412842
Mean Squared Error: 0.38855940004423717

それでも、5から6に切り替えるず、品質はわずかに向䞊したした。スペヌスを節玄するために、実隓の完党なログをスキップし、すぐに結果をテヌブルに転送しお、適切なスケゞュヌルを生成したす。

min_samples_leafMSE過ごした時間
10.394668.58
20.397068.03
30.394166.98
40.397266.96
50.393466.33
60.388566.88
70.389565.22
80.390365.89
90.392666.31




取埗した怜蚌結果に高い信頌性があるため、min_samples_leafの最適倀は6であるず想定できたす。次のパラメヌタヌを䜿甚した実隓に進みたしょう。

| min_weight_fraction_leaffloat、オプションデフォルト= 0
|
リヌフノヌドにある必芁があるすべおの入力サンプルの重みの合蚈の最小重み付き割合。サンプルを持っおいたす
| sample_weightが指定されおいない堎合、等しい重み。

シヌトを圢成するために必芁な䟋の最小郚分。デフォルトでは、それらの数はれロです。぀たり、制限は蚭定されおいたせん。理論的には、このパラメヌタヌの倀を倧きくするず、mean_samples_leafパラメヌタヌのように再トレヌニングが防止されたす。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 6 , 'random_seed': 0, 'min_weight_fraction_leaf': 0.01} 

Time spent: 68.06336092948914
Mean Squared Error: 0.41160143391833687

残念ながら、予枬の質は悪化しおいたす。しかし、私たちはあたりにも重芁芖しすぎたのでしょうか

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 6 , 'random_seed': 0, 'min_weight_fraction_leaf': 0.001} 

Time spent: 67.03254532814026
Mean Squared Error: 0.39262469473669265

繰り返したすが、枛少は私たちを助けたせんでした。䞀般的に、このパラメヌタヌの説明から刀断するず、固定サむズをサンプリングする堎合、指定されたmin_samples_leafたたは指定されたmin_weight_fraction_leafのいずれかの制限が適甚されたす。そのたたにしお、次ぞ進んでください。

|サブサンプルフロヌト、オプションデフォルト= 1.0
|個々のベヌスの適合に䜿甚されるサンプルの割合
|孊習者。 1.0より小さい堎合、これは確率的募配になりたす
。ブヌスティング「subsample」はパラメヌタヌ「n_estimators」ず察話したす。
| 「サブサンプル<1.0」を遞択するず、分散が枛少したす
。バむアスの増加。

 MODEL_SETTINGS = { 'model_name': GradientBoostingRegressor, 'learning_rate': 0.55, 'n_estimators': 100, 'max_depth': 3, 'min_samples_split': 4, 'min_samples_leaf': 6, 'random_seed': 0, 'min_weight_fraction_leaf': 0.0, 'subsample': 0.9} 

Time spent: 155.24894833564758
Mean Squared Error: 0.39231319253775626

単にパラメヌタを倉曎するだけではもはや圹に立たないように芋えたすそしお、ほずんど圹に立たないものず想定できたす。このステップを完了するために、リク゚ストを凊理する最埌の時間クラスを䜿甚しおkaggleの送信を生成したしょう。もちろん、kaggleで問題を解決するずき、オプションは通垞よりシンプルに䜿甚されたすが、実際のアプリケヌションで発生する状況をシミュレヌトし、前回曞いたクラスをテストするために、远加のコヌドを曞くのに少し時間を費やすこずができたす。

予枬ファむルを生成する


ネタバレの䞋にはgenerate_response.pyスクリプトの倉曎がありたす。

スクリプト修正
[generate_response.py]
 import pickle import numpy import research class FinalModel(object): def __init__(self, model, to_sample, additional_data): self._model = model self._to_sample = to_sample self._additional_data = additional_data def process(self, instance): return self._model.predict(numpy.array(self._to_sample( instance, self._additional_data)).reshape(1, -1))[0] if __name__ == '__main__': with open('./data/model.mdl', 'rb') as input_stream: model = pickle.loads(input_stream.read()) additional_data = research.load_additional_data() final_model = FinalModel(model, research.to_sample, additional_data) # print(final_model.process({'tube_assembly_id':'TA-00001', 'supplier':'S-0066', # 'quote_date':'2013-06-23', 'annual_usage':'0', # 'min_order_quantity':'0', 'bracket_pricing':'Yes', # 'quantity':'1'})) list_of_predictions = [] with open('./data/competition_data/test_set.csv') as input_stream: header_line = input_stream.readline() column_names = header_line[:-1].split(',') for line in input_stream: cell_values = line[:-1].split(',') # new_id = cell_values[column_names.index('id')] new_id = cell_values[0] # id column new_instance = dict(zip(column_names[1:], cell_values[1:])) new_prediction = final_model.process(new_instance) list_of_predictions.append((new_id, new_prediction)) with open('./data/output.csv', 'w') as output_stream: output_stream.write('id,cost\n') for prediction in list_of_predictions: output_stream.write(prediction[0] + ',' + str(prediction[1]) + '\n') 


スクリプトは、入力デヌタずしお、スクリプトによっお生成されたモデルず、コンテストの䞻催者によっお提䟛されたアヌカむブにあるresearch.pyテストデヌタを含むファむルを䜿甚したす。出口で圌は、ファむルgereriruet output.csv。ファむル内の予枬の圢成に関連する倉曎がないこずは、トレヌニングおよびサヌビングパむプラむンの柔軟性を瀺しおいたす。倚くの新機胜をピックアップし、モデルを倉曎し、以前は䜿甚されおいなかった新しいデヌタファむルを䜿甚したしたが、モデルのアプリケヌションに関連するコヌドの郚分は基本的に倉曎されおいたせん。test_set.csvgenerate_response.py

kaggleコンペティションはすでに終了しおいたすが、プラットフォヌムの評䟡が怜蚌の期埅にどのように䞀臎するかをここで確認できたす。

画像

結果は、䞀芋、元気づけられたす-予枬の品質は怜蚌よりもさらに高いこずが刀明したした。ただし、䞀方では、パブリックリヌダヌボヌドの結果は怜蚌の結果よりも非垞に高く、他方では、党䜓的な順䜍における競争が終了した瞬間に比べお私たちの䜍眮はただ高くありたせん。ただし、これらの問題の䞡方に察する解決策は別の機䌚にお任せしたす。

おわりに


そこで、予枬アルゎリズムを最適化し、さたざたなモデルを詊し、そのような広く宣䌝されおいるニュヌラルネットワヌクがタスクにあたり適しおいないこずを発芋し、ハむパヌパラメヌタヌの遞択を詊し、最終的にkaggleの最初の予枬ファむルを䜜成したした。1぀の蚘事に぀いおはそれほどではありたせん。たたお䌚いしたしょう。

少しオフトピック
, , . HR- . , , , , ( ). , , . , , , , - , . , . ( — ) , , , .

Source: https://habr.com/ru/post/J354154/


All Articles