高齢者の膝高からの身長推定
はじめに
株式会社Rehab for JAPAN 開発2部サイエンスチームの上田です。
栄養評価や必要エネルギー量算出には身長や体重の測定値が必須ですが、高齢者の方では歩行や直立が困難であったり、脊椎が湾曲していたりして身長を正しく測定できない場合があります。このような場合、日本では主にChumlea[1]らやMiyazawa[2]らが提唱する身長推定式が利用されています。これらの推定式は膝高と年齢を説明変数に、身長を目的変数とした重回帰式です。
本記事ではChumleaらの式を例に、重回帰分析による膝高と年齢から身長を推定する式の導出と、同様にLightGBMで回帰分析を行った結果の比較を行います。
ターゲット
- データサイエンティスト(見習いレベル)
- データサイエンティストを目指す方
- データサイエンスに興味のある方
要約
Chumleaらによる推定式
性別 | 回帰式 | ||
---|---|---|---|
男性 | 身長 = 64.19 + 2.03 * 膝高 - 0.04 * 年齢 | 0.67 | 3.84 |
女性 | 身長 = 84.88 + 1.83 * 膝高 - 0.24 * 年齢 | 0.65 | 3.50 |
における真値
- 重回帰モデルの方が汎化性能が高い
- 説明変数(特徴量)が少ないとLightGBM回帰モデルは過学習しやすい
人工データの準備
利用できそうな膝高のデータがないため、Chumleaらの式に適当な身長と年齢から膝高を計算し、推定される予定の身長に誤差を与えました。
# 適当な身長と年齢データ(年齢は65~90歳)
data
# height age
# 0 154.0 81
# 1 167.0 70
# 2 165.3 78
# 3 150.0 84
# 4 148.6 86
# .. ... ...
# 995 159.0 81
# 996 166.0 88
# 997 165.4 73
# 998 158.0 88
# 999 162.0 84
# Chumleaらの式による膝高の逆算。大元の身長には誤差を加える(小数点以下1位で丸め)。
import numpy as np
seed = 42
np.random.seed(seed)
n = data.shape[0]
data['knee_height'] = data.apply(lambda x: (x.height + 0.04 * x.age - 64.19) / 2.03, axis=1)
x = data.drop('height', axis=1)
y = round(data.height + alpha * np.random.normal(size=n),1)
学習データとテストデータの分割
今回準備したデータセットはN=1000で、学習用に800件、テスト用に200件としました。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=seed)
線形重回帰モデルとLightGBM回帰モデルによる学習
Chumleaらは、身長
実際に重回帰分析を行う際には各説明変数の単位や数値としての桁数(粒度)が異なる場合には正規化や各種スケーリングを考慮します。また説明変数が多い場合には分析に必要な適度な説明変数のみを重回帰式に含める必要があるため、「変数増加法」や「変数増減法」など説明変数の様々な選択方法を試すことになります。
今回用いた線形重回帰モデルはscikit-learnのLinearRegressionです。この他にstatsmodelsの重回帰モデルがあり、簡単に各係数の統計量等を表示できて大変便利ですが、今回はChumleaらの式を元にしたデータセットを利用するので各変数の統計量には注目しません。
同じくscikit-learnのLightGBMによる回帰分析では、評価関数としてRMSE(Root Mean Squared Error)を選択しました。
from sklearn.linear_model import LinearRegression
import lightgbm as lgb
model_lr = LinearRegression()
model_lr.fit(x_train, y_train)
model_lgb = lgb.LGBMRegressor(verbose=-1, random_satate=seed, metric='rmse')
model_lgb.fit(x_train, y_train)
学習データの精度
def see(y_true, y_pred):
return (sum((y_true - y_pred)**2)/(len(y_true)-3))**0.5
mlr_y = model_mlr.predict(x_train)
lgb_y = model_lgb.predict(x_train)
print(f'MLR: R2 = {model_mlr.score(x_train, y_train):.2f}, SEE = {see(y_train, mlr_y):.2f}')
print(f'LGB: R2 = {model_lgb.score(x_train, y_train):.2f}, SEE = {see(y_train, lgb_y):.2f}')
# MLR: R2 = 0.77, SEE = 3.84
# LGB: R2 = 0.84, SEE = 3.21
モデル | ||
---|---|---|
線形重回帰 | 0.77 | 3.84 |
LightGBM回帰 | 0.84 | 3.21 |
重回帰モデルの方は調整した通り
テストデータの精度
LightGBM回帰モデルが過学習を起こしている可能性があるため、事前に分割したテストデータによる汎化性能を見てみます。
mlr_y = model_mlr.predict(x_test)
lgb_y = model_lgb.predict(x_test)
print(f'MLR: R2 = {model_mlr.score(x_test, y_test):.2f} SEE = {see(y_test, mlr_y):.2f}')
print(f'LGB: R2 = {model_lgb.score(x_test, y_test):.2f} SEE = {see(y_test, lgb_y):.2f}')
# MLR: R2 = 0.74 SEE = 3.86
# LGB: R2 = 0.69 SEE = 4.19
モデル | ||
---|---|---|
線形重回帰 | 0.74 | 3.86 |
LightGBM回帰 | 0.69 | 4.19 |
やはり、重回帰モデルでは学習データの予測時と比べて若干落ちるものの精度を維持しているのに対して、LightGBM回帰モデルでは精度がかなり低下しています。
念ため、テストデータで外挿してしまっているか確認すると
print(x_train.agg(['min', 'max']))
print(x_test.agg(['min', 'max']))
# age knee_height
# min 65 30.9
# max 90 59.9
# age knee_height
# min 65 37.3
# max 90 57.9
テストデータの年齢、膝高はそれぞれ学習データの最小値から最大値の範囲内であることからLightGBMの精度低下は外挿によるものではない事がわかります。つまり過学習しているため汎化性能が落ちていると考えられます。
LightGBM回帰モデルの過学習を抑えた精度
LightGBM回帰モデルの学習時に学習率を抑えることで重回帰モデルと同様に
model_lgb = lgb.LGBMRegressor(verbose=-1, random_satate=seed, metric='rmse', learning_rate=0.0179)
model_lgb.fit(x_train, y_train)
lgb_y = model_lgb.predict(x_train)
print(f'LGB: R2 = {model_lgb.score(x_train, y_train):.2f} SEE = {see(y_train, lgb_y):.2f}')
lgb_y = model_lgb.predict(x_test)
print(f'LGB: R2 = {model_lgb.score(x_test, y_test):.2f} SEE = {see(y_test, lgb_y):.2f}')
# LGB: R2 = 0.77 SEE = 3.84
# LGB: R2 = 0.70 SEE = 4.12
データセット | ||
---|---|---|
学習用 | 0.77 | 3.84 |
テスト用 | 0.70 | 4.12 |
学習率を抑えた結果、学習用データを用いた精度は偶然にも重回帰モデルと一致しました。ただしテストデータを用いた精度はあまり向上しない結果となりました。
この結果から、ハイパーパラメータのチューニングによるテストデータの精度向上は可能性としてはありますが、今回のデータセットを用いた推定モデルとしては素直に重回帰モデルを用いた方が汎化性能が高いモデルを早く得られる事が判明しました。
まとめ
本記事では医療、介護現場などで良く利用される膝高と年齢から身長を推定する式を重回帰モデルとLightGBM回帰モデルにより導きました。結果として重回帰モデルの方が汎化性能が高くなりましたが、理由として
- 説明変数が膝高と年齢と非常に少なかった事
- 重回帰モデルの切片、回帰係数の導出にほぼ一意的に解けてしまう最小二乗法を使用
が挙げられます。一方、LightGBM回帰モデルで説明変数が少ない場合には過学習を抑える別の方法として、num_leavesやmax_depthなどを制限してツリー構造を単純化することが考えられます。また今回は外挿による影響を調べませんでしたが機会があればチャレンジしたいと思います。
Discussion