十个实用的机器学习建议

淡淡的烟草味﹌ 2023-07-23 03:45 15阅读 0赞

声明

本博客来源于: 算法猿的成长

1. 学会写抽象类

当你开始写抽象类的时候,你就知道它可以让你的代码库变得很清晰明了,它们会强制采用一样的方法和方法名字。如果同个项目有多个人参与,并且每个人都用不同方法,那会产生不必要的混乱情况。

下面是一个代码例子:

  1. import os
  2. from abc import ABCMeta, abstractmethod
  3. class DataProcessor(metaclass=ABCMeta):
  4. """Base processor to be used for all preparation."""
  5. def __init__(self, input_directory, output_directory):
  6. self.input_directory = input_directory
  7. self.output_directory = output_directory
  8. @abstractmethod
  9. def read(self):
  10. """Read raw data."""
  11. @abstractmethod
  12. def process(self):
  13. """Processes raw data. This step should create the raw dataframe with all the required features. Shouldn't implement statistical or text cleaning."""
  14. @abstractmethod
  15. def save(self):
  16. """Saves processed data."""
  17. class Trainer(metaclass=ABCMeta):
  18. """Base trainer to be used for all models."""
  19. def __init__(self, directory):
  20. self.directory = directory
  21. self.model_directory = os.path.join(directory, 'models')
  22. @abstractmethod
  23. def preprocess(self):
  24. """This takes the preprocessed data and returns clean data. This is more about statistical or text cleaning."""
  25. @abstractmethod
  26. def set_model(self):
  27. """Define model here."""
  28. @abstractmethod
  29. def fit_model(self):
  30. """This takes the vectorised data and returns a trained model."""
  31. @abstractmethod
  32. def generate_metrics(self):
  33. """Generates metric with trained model and test data."""
  34. @abstractmethod
  35. def save_model(self, model_name):
  36. """This method saves the model in our required format."""
  37. class Predict(metaclass=ABCMeta):
  38. """Base predictor to be used for all models."""
  39. def __init__(self, directory):
  40. self.directory = directory
  41. self.model_directory = os.path.join(directory, 'models')
  42. @abstractmethod
  43. def load_model(self):
  44. """Load model here."""
  45. @abstractmethod
  46. def preprocess(self):
  47. """This takes the raw data and returns clean data for prediction."""
  48. @abstractmethod
  49. def predict(self):
  50. """This is used for prediction."""
  51. class BaseDB(metaclass=ABCMeta):
  52. """ Base database class to be used for all DB connectors."""
  53. @abstractmethod
  54. def get_connection(self):
  55. """This creates a new DB connection."""
  56. @abstractmethod
  57. def close_connection(self):
  58. """This closes the DB connection."""

2. 固定好随机种子

实验的可复现是非常重要的一件事情,而随机种子可能会造成实验结果无法复现。因此必须固定好随机种子,否则会导致不同的训练集和测试集,以及神经网络的不同初始化权重,这些都会导致不一样的实验结果。

  1. def set_seed(args):
  2. random.seed(args.seed)
  3. np.random.seed(args.seed)
  4. torch.manual_seed(args.seed)
  5. if args.n_gpu > 0:
  6. torch.cuda.manual_seed_all(args.seed)

3. 先展示少量数据

如果你的数据量非常大,然后你接下来要做的工作是类似于清洗数据或者建模,那么可以每次采用少量的数据来避免一次加载大量的数据。当然这个做法的前提是你只是希望测试代码,而不是实际开始实现相应的工作。

这个做法非常实用,特别是你本地电脑的配置不足以加载全部数据集的时候,但你又想在本地电脑采用 Jupyter/ VS code/ Atom 做实验。

代码例子如下:

  1. df_train = pd.read_csv(‘train.csv’, nrows=1000)

4. 预估可能的错误(一个成熟开发者的标志)

记得每次都要检查数据是否存在空数据(NA),因为这将带来代码出错。当然,即便当前数据中不存在,这并不意味着在后续的训练步骤中不会出现这种情况,所以需要保持这种检查。

比如采用下述的代码:

  1. print(len(df))
  2. df.isna().sum()
  3. df.dropna()
  4. print(len(df))

5. 展示处理进度

当在处理大量的数据的时候,如果能够知道总共需要的时间以及当前的处理进度是非常有帮助的。

这里有几种方法:

第一种方法:采用 tqdm 库,代码例子:

  1. from tqdm import tqdm
  2. import time
  3. tqdm.pandas()
  4. df['col'] = df['col'].progress_apply(lambda x: x**2)
  5. text = ""
  6. for char in tqdm(["a", "b", "c", "d"]):
  7. time.sleep(0.25)
  8. text = text + char

在这里插入图片描述
第二种方法–fastprogress

  1. from fastprogress.fastprogress import master_bar, progress_bar
  2. from time import sleep
  3. mb = master_bar(range(10))
  4. for i in mb:
  5. for j in progress_bar(range(100), parent=mb):
  6. sleep(0.01)
  7. mb.child.comment = f'second bar stat'
  8. mb.first_bar.comment = f'first bar stat'
  9. mb.write(f'Finished loop {i}.')

在这里插入图片描述
6. Pandas 可能会很慢

如果你采用 pandas 库,那么你会发现有的时候它的速度会有多慢,特别是采用 groupby 函数的时候。不需要想尽办法来寻找更好的加速方法,只需要修改一行代码即可,如下所示,采用 modin 即可解决这个问题:

  1. import modin.pandas as pd

7. 计算函数运行的时间

不是所有的函数运行时间都是一样的。

即便你的代码都跑通了,但也不表示你写出一手好代码。有些软性错误(soft-bugs)可能会导致你的代码运行算的变慢,因此很有必要找到这些问题。可以采用下述装饰器来打印函数的运行时间。

  1. import time
  2. from functools import wraps
  3. def timing(f):
  4. """Decorator for timing functions
  5. Usage:
  6. @timing
  7. def function(a):
  8. pass
  9. """
  10. @wraps(f)
  11. def wrapper(*args, **kwargs):
  12. start = time.time()
  13. result = f(*args, **kwargs)
  14. end = time.time()
  15. print('function:%r took: %2.4f sec' % (f.__name__, end - start))
  16. return result
  17. return wrapper

使用的例子如下所示:

  1. from time import sleep
  2. @timing
  3. def count_nums():
  4. sleep(1)

主函数:

  1. count_nums()

结果如下所示:
function:‘count_nums’ took: 1.0001 sec

8. 不要在云服务器方面花太多钱

没有人会喜欢一个浪费云资源的工程师

有些实验是需要跑上数十个小时的,这些实验很难进行跟踪并在实验完成的时候关闭云服务器的实例。我曾经犯过这样的错误,同时也看到其他人也曾经因为这个忘记关闭好几天。

这种情况通常发现在周五的时候,然后让其一直运行到周一。

为了避免发生这种情况,可以在代码的最后加入下列代码。不过,注意把主要代码放在 try catch中进行捕获异常,以防止发生错误。这种情况也是很可能发生的。

  1. import os
  2. def run_command(cmd):
  3. return os.system(cmd)
  4. def shutdown(seconds=0, os='linux'):
  5. """Shutdown system after seconds given. Useful for shutting EC2 to save costs."""
  6. if os == 'linux':
  7. run_command('sudo shutdown -h -t sec %s' % seconds)
  8. elif os == 'windows':
  9. run_command('shutdown -s -t %s' % seconds)

9. 创建并保存报告

在训练模型后,所有的想法最终都是来自错误和评判指标的分析。因此需要创建并保存好一个格式不错的报告,以便进行汇报。
下面是一个代码例子:

  1. import json
  2. import os
  3. from sklearn.metrics import (accuracy_score, classification_report,
  4. confusion_matrix, f1_score, fbeta_score)
  5. def get_metrics(y, y_pred, beta=2, average_method='macro', y_encoder=None):
  6. if y_encoder:
  7. y = y_encoder.inverse_transform(y)
  8. y_pred = y_encoder.inverse_transform(y_pred)
  9. return {
  10. 'accuracy': round(accuracy_score(y, y_pred), 4),
  11. 'f1_score_macro': round(f1_score(y, y_pred, average=average_method), 4),
  12. 'fbeta_score_macro': round(fbeta_score(y, y_pred, beta, average=average_method), 4),
  13. 'report': classification_report(y, y_pred, output_dict=True),
  14. 'report_csv': classification_report(y, y_pred, output_dict=False).replace('\n','\r\n')
  15. }
  16. def save_metrics(metrics: dict, model_directory, file_name):
  17. path = os.path.join(model_directory, file_name + '_report.txt')
  18. classification_report_to_csv(metrics['report_csv'], path)
  19. metrics.pop('report_csv')
  20. path = os.path.join(model_directory, file_name + '_metrics.json')
  21. json.dump(metrics, open(path, 'w'), indent=4)

10. 写好接口

你可以很好完成数据清理和训练模型,但是也还可能在最后制造很大的错误,比如没有写好服务接口。我的经验告诉我,很多人其实不知道如果写出一个好的服务接口,文档说明和服务安装配置。后面我会写另一篇文章介绍,但现在先简单介绍一下。

你可以很好完成数据清理和训练模型,但是也还可能在最后制造很大的错误,比如没有写好服务接口。我的经验告诉我,很多人其实不知道如果写出一个好的服务接口,文档说明和服务安装配置。后面我会写另一篇文章介绍,但现在先简单介绍一下。

  • Fastest–采用 fastapi 写接口是非常快速的,正如[1]中的报告展示,以及原因可以参考[2];
  • 文档— fastapi有免费的官方文档以及可以通过 http:url/docs 进行测试,并且这个链接可以自动生成以及随着我们修改代码而自动改变;
  • Workers–采用 gunicorn 服务器部署接口是因为它具有开始多于 1 个 worker 的功能,并且你应该至少保持 2 个。

运行下列命令可以部署使用 4 个 wokers ,另外可以通过测试来优化这个 workers 的数量。

  1. pip install fastapi uvicorn gunicorn
  2. gunicorn -w 4 -k uvicorn.workers.UvicornH11Worker main:app

一个运行例子如下图所示:

在这里插入图片描述
参考

[1]. Web Framework Benchmarks
[2]. https://fastapi.tiangolo.com/benchmarks/

发表评论

表情:
评论列表 (有 0 条评论,15人围观)

还没有评论,来说两句吧...

相关阅读