scikit-learnとは?scikit-learnを使って機械学習を実践しよう

- システム
エンジニア - Pythonの機械学習ライブラリであるscikit-learnの使い方を教えてください。
- プロジェクト
マネージャー - 分かりました。scikit-learnを使った機械学習について解説いたしましょう。
scikit-learnとは?
scikit-learnとは、プログラミング言語Pythonを用いてオープンソースで開発された機械学習ライブラリです。機械学習とはコンピュータが自動的に反復的にデータを学習し、そのデータに潜むパターンを見つけ出すことです。
ここではscikt-learnを利用してデータ解析のいろはのいを学んでいきます。
scikit-learnのインストール
それではscikit-learnをインストールします。開発環境はWindows 10、Python3です。Pythonのライブラリ、NumPy、matplotlib、pandasがインストールされているものとします。
もし、ライブラリがインストールされていなければ、これからscikit-learnをインストールするため、同じようにpipでインストールしてください。PowerShellを立ち上げます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
PS C:\> pip install scikit-learn Collecting scikit-learn Downloading scikit_learn-0.24.1-cp39-cp39-win_amd64.whl (6.9 MB) |████████████████████████████████| 6.9 MB 242 kB/s Collecting scipy>=0.19.1 Downloading scipy-1.6.1-cp39-cp39-win_amd64.whl (32.7 MB) |████████████████████████████████| 32.7 MB 930 kB/s Requirement already satisfied: numpy>=1.13.3 in e:\programs\python\python39\lib\site-packages (from scikit-learn) (1.19.5) Collecting threadpoolctl>=2.0.0 Downloading threadpoolctl-2.1.0-py3-none-any.whl (12 kB) Collecting joblib>=0.11 Downloading joblib-1.0.1-py3-none-any.whl (303 kB) |████████████████████████████████| 303 kB 1.6 MB/s Installing collected packages: threadpoolctl, scipy, joblib, scikit-learn Successfully installed joblib-1.0.1 scikit-learn-0.24.1 scipy-1.6.1 threadpoolctl-2.1.0 |
これでインストール完了です。
データセットの準備
scikit-learnにはBoston house prices datasetという、米国ボストンの506の地域毎の住環境から作られたデータセットがデフォルトで収録されています。このデータセットを使って機械学習のさわりを学んでいきます。
元データは次のboston.txtです。そこに、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Variables in order: CRIM per capita crime rate by town ZN proportion of residential land zoned for lots over 25,000 sq.ft. INDUS proportion of non-retail business acres per town CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) NOX nitric oxides concentration (parts per 10 million) RM average number of rooms per dwelling AGE proportion of owner-occupied units built prior to 1940 DIS weighted distances to five Boston employment centres RAD index of accessibility to radial highways TAX full-value property-tax rate per $10,000 PTRATIO pupil-teacher ratio by town B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town LSTAT % lower status of the population MEDV Median value of owner-occupied homes in $1000's |
という属性があります。
回帰問題
上記のデータセットの属性のMEDVを除いた13の指標から、MEDVを予測するという回帰問題を行ってみましょう。このデータセットはscikit-learnのload_boston()関数で呼び出すことが可能です。
1 2 3 4 5 |
from sklearn.datasets import load_boston dataset = load_boston() |
読み込んだデータセットはdateとtargetという属性を持っています。それぞれに入力値と目標値を並べたdnarrayに格納されています。これをx、tとして格納します。
1 2 3 4 5 6 7 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target |
データセットの確認
入力値が格納されたxは506個の13次元ベクトルでデータが格納されていて、tにはデータが506次元ベクトルで格納されています。
1 2 3 4 5 6 7 8 9 10 11 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target print(x.shape) print(t.shape) |
これをsample.pyとして作業フォルダに保存します。
1 2 3 |
PS C:\(作業フォルダ)>py sample.py (506, 13) (506,) |
このように出力されれば、OKです。
データセットの分割
データセットを訓練用とテスト用に分割します。機械学習では訓練用としてコンピュータに学習させておき、テスト用のデータセットから解析する作業を行います。このデータの分割はホールドアウト法と呼ばれていて、scikit-learnにはその関数が用意されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) |
これがコンパイル可能かどうか確認すると、コンパイルが通ります。ここでtrain_test_split()関数の引数test_sizeに0.3を指定したことでテスト用データセットの割合は30%に、残りの70%は自動的に訓練用データセットに割り当てられます。
モデル・目的関数・最適化手法を決定する
scikit-learnで重回帰分析を行う場合、LinerRegressionクラスを使用します。scikit.liner_model以下のLinerRegressionクラスを読み込んでインスタンスを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() |
LinerRegressionは最小二乗法を行うクラスで、目的関数や最適化手法があらかじめ用意されているのです。
モデルの訓練
scikit-learnにはモデルの訓練用に用意されているものにfit()メソッドがあります。fit()メソッドは再利用可能なコードが書きやすくなっています。reg_modelを用いて訓練をする場合、fit()メソッドの引数に入力値x、目標値tを指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target print(x.shape) print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) |
パラメータの確認
重回帰分析では重みのパラメータはw、バイアスのパラメータはbです。求められたwの値はmodel.coef_に、bの値はmodel.intercept_に格納されています。次のプログラムで値を確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) # 訓練後のパラメータ w print(reg_model.coef_) # 訓練後のバイアス b print(reg_model.intercept_) |
以下の通りに出力されました。
1 2 3 4 5 |
[-1.21310401e-01 4.44664254e-02 1.13416945e-02 2.51124642e+00 -1.62312529e+01 3.85906801e+00 -9.98516565e-03 -1.50026956e+00 2.42143466e-01 -1.10716124e-02 -1.01775264e+00 6.81446545e-03 -4.86738066e-01] 37.937107741833785 |
精度の検証
モデルの訓練が終わったら、精度の検証を行います。LinerRegressionクラスにはscore()メソッドが提供されていて、これにより入力値と目標値を与えると訓練済みのモデルを用いて計算された決定係数(coefficient of determination)という指標を返します。
検証のソースコードは次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 print(reg_model.score(x_train, t_train)) |
次のように値が出力されました。
1 |
0.7645451026942549 |
つまり、決定係数はおよそ0.765ということです。
推論
新たな入力値を与えて、予測値を計算させるにはpredict()メソッドを利用します。
ここではreg_modelからサンプルを1つ取り出して推論を行います。このとき、predict()メソッドに与える入力値のdnarrayの形は(サンプルサイズ, 各サンプルの次元数)になっていることが必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) reg_model.predict(x_test[:1]) print(reg_model.predict(x_test[:1])) |
これは次のように出力されます。
1 |
[24.9357079] |
この入力値に対して目標値は次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) reg_model.predict(x_test[:1]) #print(reg_model.predict(x_test[:1])) print(t_test[0]) |
これは次のように出力されました。
1 |
22.6 |
つまり、22.6の目標値に対して先に出力で見たおよそ24.94という予測値が返ってきたことになります。
テスト用データセットでの評価
訓練済みモデルの性能を、テスト用データセットでの決定係数を計算することで評価してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) reg_model.predict(x_test[:1]) #print(reg_model.predict(x_test[:1])) #print(t_test[0]) print(reg_model.score(x_test, t_test)) |
これは次のように出力されました。
1 |
0.6733825506400153 |
先に算出した訓練用データセットでの値、0.765よりも低い値が出てしまいました。予測値と目標値の差が大きくなってしまう現象を過学習といいます。
前処理による改善
前処理(preprocession)とは、欠損値の補完や正規化などの処理を訓練の開始前にデータセットに対して行うことです。ここでは入力変数ごとに平均が0,分散が1になるように標準化(standardization)を行います。
scikit-learnには、sklearn.preprocessingというモジュール以下にStandardScalerクラスが定義されています。このクラスを用いて標準化を行います。次のコードをsample.pyの先頭に書き足します。
1 2 3 4 5 |
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() |
StandardScalerクラスのインスタンスを作るのです。そして、fit()メソッドを用いて平均・分散の値を計算します。
1 2 3 4 5 6 7 |
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(x_train) |
計算された平均値がmean_属性に、分散値がvar_属性に格納されます。それを確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 #reg_model = LinearRegression() # モデルの訓練 #reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) #reg_model.predict(x_test[:1]) #print(reg_model.predict(x_test[:1])) #print(t_test[0]) #print(reg_model.score(x_test, t_test)) scaler.fit(x_train) # 平均 print(scaler.mean_) # 分散 print(scaler.var_) |
これは次の通りに出力されました。
1 2 3 4 5 6 7 8 |
[3.35828432e+00 1.18093220e+01 1.10787571e+01 6.49717514e-02 5.56098305e-01 6.30842655e+00 6.89940678e+01 3.76245876e+00 9.35310734e+00 4.01782486e+02 1.84734463e+01 3.60601186e+02 1.24406497e+01] [6.95792305e+01 5.57886665e+02 4.87753572e+01 6.07504229e-02 1.33257561e-02 4.91423928e-01 7.83932705e+02 4.26314655e+00 7.49911344e+01 2.90195600e+04 4.93579208e+00 7.31040807e+03 4.99634123e+01] |
これらの平均値・分散値を使ってデータセットを標準化するにはtransform()メソッドを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 #reg_model = LinearRegression() # モデルの訓練 #reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) #reg_model.predict(x_test[:1]) #print(reg_model.predict(x_test[:1])) #print(t_test[0]) #print(reg_model.score(x_test, t_test)) scaler.fit(x_train) # 平均 #print(scaler.mean_) # 分散 #print(scaler.var_) x_train_scaled = scaler.transform(x_train) x_test_scaled = scaler.transform(x_test) reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train_scaled, t_train) # 精度の検証(訓練データ) print(reg_model.score(x_train_scaled, t_train)) # 精度の検証(テストデータ) print(reg_model.score(x_test_scaled, t_test)) |
これは次のように出力され、標準化できていません。
1 2 |
0.7645451026942549 0.6733825506400195 |
べき変換による標準化
べき変換を用いて別の前処理を行います。ソースコードは次の通りです。最初の箇所を変えています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#from sklearn.preprocessing import StandardScaler #scaler = StandardScaler() from sklearn.preprocessing import PowerTransformer scaler = PowerTransformer() from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # モデルの定義 #reg_model = LinearRegression() # モデルの訓練 #reg_model.fit(x_train, t_train) # 訓練後のパラメータ w #print(reg_model.coef_) # 訓練後のバイアス b #print(reg_model.intercept_) # 精度の検証 #print(reg_model.score(x_train, t_train)) #reg_model.predict(x_test[:1]) #print(reg_model.predict(x_test[:1])) #print(t_test[0]) #print(reg_model.score(x_test, t_test)) scaler.fit(x_train) # 平均 #print(scaler.mean_) # 分散 #print(scaler.var_) x_train_scaled = scaler.transform(x_train) x_test_scaled = scaler.transform(x_test) reg_model = LinearRegression() # モデルの訓練 reg_model.fit(x_train_scaled, t_train) # 精度の検証(訓練データ) print(reg_model.score(x_train_scaled, t_train)) # 精度の検証(テストデータ) print(reg_model.score(x_test_scaled, t_test)) |
これは次のように出力されました。
1 2 3 4 |
C:\Programs\Python\Python39\lib\site-packages\sklearn\preprocessing\_data.py:3237: RuntimeWarning: divide by zero encountered in log loglike = -n_samples / 2 * np.log(x_trans.var()) 0.7859862562650238 0.7002856552456189 |
警告は出ましたが、過学習は補正されています。
パイプライン化
前処理のときにscaler、重回帰分析を行うreg_modelは、ともにfit()メソッドを用いますが、scikit-learnではパイプラインという統一する機能があります。ソースコードは次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
from sklearn.preprocessing import PowerTransformer #scaler = PowerTransformer() from sklearn.pipeline import Pipeline from sklearn.datasets import load_boston dataset = load_boston() x = dataset.data t = dataset.target #print(x.shape) #print(t.shape) # データセットを分割する関数の読み込み from sklearn.model_selection import train_test_split # 訓練用データセットとテスト用データセットへの分割 x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.3, random_state=0) from sklearn.linear_model import LinearRegression # パイプラインの作成 (scaler -> svr) pipeline = Pipeline([ ('scaler', PowerTransformer()), ('reg', LinearRegression()) ]) # scaler および reg を順番に使用 pipeline.fit(x_train, t_train) # 訓練用データセットを用いた決定係数の算出 print(pipeline.score(x_train, t_train)) # テスト用データセットを用いた決定係数の算出 linear_result = pipeline.score(x_test, t_test) print(linear_result) |
これは次のように出力されました。
1 2 3 4 |
C:\Programs\Python\Python39\lib\site-packages\sklearn\preprocessing\_data.py:3237: RuntimeWarning: divide by zero encountered in log loglike = -n_samples / 2 * np.log(x_trans.var()) 0.7859862562650238 0.7002856552456189 |
過学習は補正されて標準化されました。パイプラインを使うとx_train_scaledなどの中間変数を使うことなく、コードが簡素化されます。コードが簡素化されるとミスが格段に減るた為、パイプライン化はこの場合有効です。
- システム
エンジニア - 機械学習に興味があるので、scikit-learnは何とか習得したいですね。
- プロジェクト
マネージャー - 今回は重回帰解析を取り上げてscikit-learnの使い方を解説しましたが、機械学習に興味があるならぜひscikit-learnを学んでみてください。
scikit-learnを使って機械学習を実践しよう
Pythonの機械学習ライブラリのscikit-learnを使うと、様々な機械学習が簡単にできます。ここではほんの一部の重回帰解析という機械学習を取り上げました。機械学習に興味のある方は色々なパターンの機械学習をscikit-learnで実践してみて下さい。
FEnet.NETナビ・.NETコラムは株式会社オープンアップシステムが運営しています。
株式会社オープンアップシステムはこんな会社です
秋葉原オフィスには株式会社オープンアップシステムをはじめグループのIT企業が集結!
数多くのエンジニアが集まります。

-
スマホアプリから業務系システムまで
スマホアプリから業務系システムまで開発案件多数。システムエンジニア・プログラマーとしての多彩なキャリアパスがあります。
-
充実した研修制度
毎年、IT技術のトレンドや社員の要望に合わせて、カリキュラムを刷新し展開しています。社内講師の丁寧なサポートを受けながら、自分のペースで学ぶことができます。
-
資格取得を応援
スキルアップしたい社員を応援するために資格取得一時金制度を設けています。受験料(実費)と合わせて資格レベルに合わせた最大10万円の一時金も支給しています。
-
東証プライム上場企業グループ
オープンアップシステムは東証プライム上場「株式会社オープンアップグループ」のグループ企業です。
安定した経営基盤とグループ間のスムーズな連携でコロナ禍でも安定した雇用を実現させています。
株式会社オープンアップシステムに興味を持った方へ
株式会社オープンアップシステムでは、開発系エンジニア・プログラマを募集しています。
年収をアップしたい!スキルアップしたい!大手の上流案件にチャレンジしたい!
まずは話だけでも聞いてみたい場合もOK。お気軽にご登録ください。


新着案件New Job
開発エンジニア/東京都品川区/【WEB面談可】/在宅ワーク
月給29万~30万円東京都品川区(大崎駅)遠隔テストサービス機能改修/JavaScript/東京都港区/【WEB面談可】/テレワーク
月給45万~60万円東京都港区(六本木駅)病院内システムの不具合対応、保守/東京都豊島区/【WEB面談可】/テレワーク
月給30万~30万円東京都豊島区(池袋駅)開発/JavaScript/東京都豊島区/【WEB面談可】/テレワーク
月給50万~50万円東京都豊島区(大塚駅)債権債務システム追加開発/東京都文京区/【WEB面談可】/在宅勤務
月給62万~67万円東京都文京区(後楽園駅)PMO/東京都豊島区/【WEB面談可】/在宅勤務
月給55万~55万円東京都豊島区(池袋駅)