CNNによるMNISTの埋め込み(t-SNEも)

  • 前回に続いて,学習済みモデルの中間層の出力を取り出す例として,学習済みCNNを自己符号化(オートエンコーダ)として使う例をやってみる。
  • コードはPyTorch公式exampleのmnistを少しいじって,モデルの中間層が取り出しやすくして,t-SNEによる埋め込みを行った。と言っても,t-SNEはsklearnを使っているだけ。t-SNEはアルゴリズム的に学習済みモデルを使って予測,という類の使い方ではない。つまり,他のsklearnのモデルみたいに学習データでfitして,評価したいデータをpredict,みたいな使い方は(基本的には)できない。
from __future__ import print_function
import numpy as np
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.encoder = nn.Sequential(
            # Conv1
            nn.Conv2d(1, 10, kernel_size=5),
            nn.MaxPool2d(kernel_size=2),
            nn.ReLU(),
            # Conv2
            nn.Conv2d(10, 20, kernel_size=5),
            nn.Dropout2d(),
            nn.MaxPool2d(kernel_size=2),
            nn.ReLU(),
        )
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = self.encoder(x)
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

def train(args, model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

def test(args, model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            # sum up batch loss
            test_loss += F.nll_loss(output, target, reduction='sum').item()
            # get the index of the max log-probability
            pred = output.max(1, keepdim=True)[1]
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

def auto_encode(args, model, device, test_loader):
    model.eval()
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            latent_vecs = model.encoder(data)
            latent_vecs = latent_vecs.view(-1, 320)
            latent_vecs = model.fc1(latent_vecs)
    return latent_vecs, target

def main(train=False):
    # Training settings
    parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
    parser.add_argument('--batch-size', type=int, default=64, metavar='N',
                        help='input batch size for training (default: 64)')
    parser.add_argument('--test-batch-size', type=int, default=10000, metavar='N',
                        help='input batch size for testing (default: 10000)')
    parser.add_argument('--epochs', type=int, default=10, metavar='N',
                        help='number of epochs to train (default: 10)')
    parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
                        help='learning rate (default: 0.01)')
    parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
                        help='SGD momentum (default: 0.5)')
    parser.add_argument('--no-cuda', action='store_true', default=False,
                        help='disables CUDA training')
    parser.add_argument('--seed', type=int, default=1, metavar='S',
                        help='random seed (default: 1)')
    parser.add_argument('--log-interval', type=int, default=10, metavar='N',
                        help='how many batches to wait before logging training\
                        status')
    args = parser.parse_args()
    use_cuda = not args.no_cuda and torch.cuda.is_available()

    torch.manual_seed(args.seed)

    device = torch.device("cuda" if use_cuda else "cpu")

    kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {}
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('/home/hoge/PyTorch_MLdata/MNIST',
                       train=True,
                       download=True,
                       transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))
                       ])),
        batch_size=args.batch_size, shuffle=True, **kwargs)
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('/home/hoge/PyTorch_MLdata/MNIST',
                       train=False,
                       transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))])),
        batch_size=args.test_batch_size, shuffle=True, **kwargs)

    model = Net().to(device)

    if train:
        optimizer = optim.SGD(model.parameters(), lr=args.lr,
                              momentum=args.momentum)
        for epoch in range(1, args.epochs + 1):
            train(args, model, device, train_loader, optimizer, epoch)
            test(args, model, device, test_loader)

        torch.save(model.state_dict(), './model_param_autoencoder.pkl')

    # Auto Encode using trained model
    model.load_state_dict(torch.load('./model_param_autoencoder.pkl'))
    latent_vecs, target = auto_encode(args, model, device, test_loader)
    latent_vecs, target = latent_vecs.numpy(), target.numpy()
    print(latent_vecs.shape, target.shape)
    latent_vecs_reduced = TSNE(n_components=2,\
                               random_state=0).fit_transform(latent_vecs[:1000])
    plt.scatter(latent_vecs_reduced[:, 0], latent_vecs_reduced[:, 1],
                c=target[:1000], cmap='jet')
    plt.colorbar()
    plt.show()
    
if __name__ == '__main__':
    main(train=False)

PyTorchのモデルの書き方の整理

サマリ

いくつかの書き方があるが,混乱するのは下記の種類が色々Webに見られるからか。

  • initにはパラメタありだけ,パラメタが無いものはforwardに。これが一番柔軟性があるように思う。モデルの中間層を細かく取り出したり,活性化関数を変えたりしたいならこれが良いと思う。
  • initにReLuなども書いてしまう。print(model)でReLuなども含めて出力してくれる点が良い点。
  • ③ Sequencialを使う。特定の塊ごとに意味を持たせられる。

これらは,それぞれ排他的に使う必要はなくて,適材適所でMixして使う。方針としては,学習後に分解することのない特定の塊があればどんどんSequencialに入れて,initにReLuなどのパラメタが無い層も含めて書いて良いと思う。もし,特定のレイヤを置換したりしたい場合は,それは独立したSequentialか,独立したレイヤにする。

基本事項

  • nn.Moduleを継承して、構成要素をinitに定義して順方向をforwardに記載。callを描かなくても、モデルに入力を渡せば自動的にforwardを実行してくれる。
  • Sequentialを使ったり、Activationを関数に渡してしまったり、色々なパタンがあるので整理しておく。

パタン1(最もシンプルなパタン)

  • 学習パラメタを持つものはinitに、関数はforwardに。
  • 公式チュートリアルはこの書き方。(Poolingもパラメタは無いからforward)
  • print(model)で出力されるのはinitに描かれたもの。
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MultiLayerPerceptron, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)        
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, num_classes)        
    
    def forward(self, x):
        h = F.relu(self.fc1(x))
        h = F.relu(self.fc2(h))
        out = self.fc3(h)
        return out

パタン2(ReLUなどもinitに集約)

  • パラメタ付きか否かの分離ができないが、print文でReLUなども出力されるのでその点は良い。この場合はReLUは関数(F.relu)ではなくクラス(nn.ReLU)を使う。
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MultiLayerPerceptron, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)        
        self.ac1 = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.ac2 = nn.ReLU()
        self.fc3 = nn.Linear(hidden_size, num_classes)        
    
    def forward(self, x):
        h = self.ac1(self.fc1(x))
        h = self.ac2(self.fc2(h))
        out = self.fc3(h)
        return out

パタン3(Sequential)

  • KerasのSequentialのような書き方が可能。単純なMLPやCNNならこれで良い。一方で多入力、多出力なネットワークの記述は出来ない。
model = nn.Sequential()
model.add_module('fc1', nn.Linear(input_size, hidden_size))
model.add_module('relu1', nn.ReLU())
model.add_module('fc2', nn.Linear(hidden_size, hidden_size))
model.add_module('relu2', nn.ReLU())
model.add_module('fc3', nn.Linear(hidden_size, n_classes))
  • 先にリストに層をまとめておくことも可能。 layers = [nn.Linear(input_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hiddensize, n_classes)] model = nn.Sequential(*layers)

PyTorchでのモデルの中間層へのアクセス方法

  • PyTorchにはいろいろなモデルの書き方があるけど,このメモの内容を考えると学習パラメタが無いレイヤ含めてinitにもたせておくほうが良いのかもしれない。 (と思ったけど,一番最後のレイヤをとりあえずコピーしてforwardを再定義するやり方ならどっちでも良い,と思った)

重みにアクセスしたい場合

  • 重みを特定の値で初期化した場合などが利用としては考えられるか。
  • model.state_dict()が簡単。
  • 辞書で返してくれるのでmodelのinitに付けた名前でアクセスが可能。
model = Net()
model.state_dict() #=> {'conv1.weight':[...], 'conv1.bias':[...],...}

中間層の出力にアクセスしたい場合

  • forwardのhookしたり,色々やり方はあるようだけど,モデルの構築の段階からSequencialを使って分割して作成しておくのがわかりやすい。
  • 隠層の出力をそれぞれ得たい,という場合には後述のようにforwardを再定義するような方法があるようだ。
  • サンプルとしては,torchvisionのモデルが参考になる。例えば,AlexNetは特徴量抽出までのfeaturesと,それを使って分類するclassifeirに分けて実装されている。例えば,AlexNetは下記のようになっている。
class AlexNet(nn.Module):

    def __init__(self, num_classes=1000):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), 256 * 6 * 6)
        x = self.classifier(x)
    return x
  • これを用いて,学習済みのAlexNetの特徴量抽出までを使用したければ,下記のように使える。
model = models.alexnet(pretrained=True)
y = model.features(x)
  • VGGの各層からの出力を得るには下記のような例がある。ここでは,各層ではなく,特定のReLU層,3,8,15,22を出力している。やっていることは,VGG16のfeaturesのレイヤをそれぞれ通して行きながら,その過程で所望のレイヤの出力を保存している。
  • このやり方は結構汎用的だ。学習済みのモデルをとりあえずもってきて,自分でfowardを書き直しているイメージ。必要なら,適時dropoutを新たに挟む,とかも出来る。
import torch
import torch.nn as nn
from torchvision.models import vgg16
from collections import namedtuple

class Vgg16(torch.nn.Module):
    def __init__(self):
        super(Vgg16, self).__init__()
        features = list(vgg16(pretrained = True).features)[:23]
        self.features = nn.ModuleList(features).eval() 
        
    def forward(self, x):
        results = []
        for ii,model in enumerate(self.features):
            x = model(x)
            if ii in {3,8,15,22}:
                results.append(x)
        return results

CNN in MNIST with PyTorch (PyTorchの基本メモ)

  • PyTorchのチュートリアルとexampleはとても参考になる。0.4から色々変わっているようだし,改めて情報を整理する。
  • まず今回は,MNISTを動かしながら以下の項目についてメモしておく。
    1. MNISTデータのロード方法。可視化方法。
    2. ネットワーク,学習,テストの書き方。
    3. モデルの学習と保存

MNISTデータの利用方法

  • 生のMNISTはバイナリで扱いが面倒。なので,torchvisionのAPIを活用する。(tf.keras, chainerも同様のAPIがあるようだ。)
  • torchvisionはMNISTの他にも,Fasion-MNIST, EMNIST, Cifar-10, STL-10などいくつかのデータセットのダウンロードAPIを用意している。
  • 注意点として,MINST/Fashin-MNIST/EMNISTは同じクラスで実装されているので,ダウンロードで同じディレクトリを指定すると,既存のデータ(/processed/train.ptなど)がすでにあるのでダウンロードをしないことになる。よって,MNIST,EMNIST,Fashion-MNIST毎にrootディレクトリは変更する。transformはダウンロードしてtrain.ptなどを作る段階で適用するわけじゃないので,transformの違いは問題ない。

ダウンロード

  • 下記のコードを実行してMNISTをダウンロードする。rootはデータセットの格納ディレクトリ, downloadをTrueにするとWebから取得する。
  • trainはTrueにするとroot/processed/train.ptからデータを生成し,Falseの場合にはroot/processed/test.pyから生成する。
  • transformはデータに対して共通で適用する前処のの関数を渡す。関数はPILImageを1つ受け取って変換後の画像を返す関数。
from torchvision import datasets, transforms

def main():
    mnist_train = datasets.MNIST(
        root='/home/hoge/PyTorch_MLdata/EMNIST',
        download=True,
        train=True,
        transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.1307,), (0.3081,))
        ]))
    print(mnist)

    mnist_test = datasets.MNIST(
        root='/home/hoge/PyTorch_MLdata/MNIST',
        download=True,  # If already exist, download is ignored.
        train=False,
        transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.1307,), (0.3081,))
        ]))        

if __name__ == '__main__':
    main()        
  • 上記のコードを実行するとrootに指定したディレクトリに下記が生成されている。
- root_dir
  - processed
    - test.pt                  # テスト用データがPyTorchデータ向けに生成されている。
    - training.pt              # 画像用データがPyTorchデータ向けに生成されている。
  - raw
    - t10k-images-idx3-ubyte   # テスト用画像
    - t10k-labels-idx1-ubyte   # テスト用ラベル
    - train-images-idx3-ubyte  # 学習用データ画像
    - train-labels-idx1-ubyte  # 学習用データラベル

データローダー

  • torchにはデータローダというバッチ学習に便利なものがある。単純化以外にも読み込みのマルチスレッド化なども処理してくれるので活用するべき。
  • DataSetはデータの集合。集合ではるが,getitemなどが実装されている。DataLoaderはDataSetを内部に持っていて,バッチ単位で分割してくれるイテレータ
mnist_train_loader = torch.utils.data.DataLoader(mnist_train,
                                                 batch_size=32,
                                                 shuffle=True)
mnist_test_loader = torch.utils.data.DataLoader(mnist_test,
                                                batch_size=32,
                                                shuffle=True)
  • MNISTのようなデータ以外に,自分で入力集合Xsと出力集合Ysを生成した場合,それをTensorDatasetにして渡せばデータローダは簡単に使える。渡す際にはXs, Ysは0次元目のサイズが揃ったtorch.tensorであることが条件。0次元目を軸にgetitemされるからね。 (公式Documentを見た限りだと,別に渡すテンソルは2つに限定されず,n個渡せば,そのn個をローダで良きに取り出してくれるみたいだ。)
import torch.utils.data as Data
# x, y are input and label data (MUST be Pytorch Tensors)
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
    dataset=torch_dataset,
    batch_size=32,
    shuffle=True,
    num_workers=2 # subprocess for loading
)
for epoch in range(n_epoch):
    for step, (batch_x, batch_y) in enumerate(loader):
        # training using batch_x, batch_y
        ...
  • ところで,データセットから直接サブセットを取得したい場合に,datasetはスライスオペレータをサポートしていないので少し工夫がいる。PyTorchのForcumに参考になるコードがある。
# データセットmnist_testから1000個取り出す場合
imgs, labels = zip(*[mnist_test[i] for i in range(1000)])

MNISTデータ可視化

  • あまり本論とは関係ないけれど,MNISTの各データ,ラベルの形式を理解するためにも可視化をしてみる。
  • datasetの返り値は(image:torch.Tensor, label:torch.Tensor)になっている。(というよりも,datasetの生成時にtransform.ToTensorを使ってそうしている)。
  • imageは(1,28,28)のテンソルなので,numpyに変換してmatplotlibのimshowに渡せば描画してくれる。ただし,imshowは(H,W), (H,W,3), (H,W,4)の形式で,各要素は0..1のfloatか0..255の整数でなければならないことに注意する。
image, label = mnist_train[0]  # Tensor(1,28,28), Tensor(1)
image_np = image.numpy()       
plt.imshow(image_np.reshape(28,28))
plt.show()

Network,学習,テストの書き方

  • exampleを見ると,ネットワークをクラスで,train/testはそれぞれ関数として実装している。
  • ネットワークの書き方は色々あるが,exampleでは学習パラメタを含むものをinitに書いて,純粋な関数(relu, poolingなど)はforward層で書いている。(nn.Dropout2dもパラメタ無いはずでは?と思うが。)
  • train, testの書き方は参考になる。deviceはGPU/CPUでコードを共通化する書き方。to(device)で必要だったらGPUテンソル化してくる。
  • train/testで重要な項目の1つが不要なところで勾配計算をさせないこと。テンソルの計算は基本的に計算グラフが構成され,計算のたびに値が保存されてメモリを消費する。それを避けるために,trainの中ではloss.itemでlossの値を抽出している。running_lossを計算するとき名などにテンソルのまま計算してしまうと無駄にメモリを消費するので避ける。また,testのようにそもそも勾配計算が不要な場合にはwith no_grad()でrequire_gradをFalseにしている。
  • また,PyTorchのクロスエントロピー周りは少し注意が必要。functional.cross_entropyはnll_lossとsoftmaxを組合せた関数。モデル側にsoftmaxを入れて,損失関数には単純に負のlog尤度関数(negative-log-likelihood)を使う場合にはnll_lossを使う。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

def train(args, model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

def test(args, model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            # sum up batch loss
            test_loss += F.nll_loss(output, target, reduction='sum').item()
            # get the index of the max log-probability
            pred = output.max(1, keepdim=True)[1]
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

学習とモデルの保存

  • 後は実際に必要なエポック数だけ学習を回して,モデルを保存するだけ。
model = Net().to(device)
optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)

for epoch in range(1, args.epochs + 1):
    train(args, model, device, train_loader, optimizer, epoch)
    test(args, model, device, test_loader)

# パラメタだけを保存(推奨)
torch.save(model.state_dict(), 'model_param.pkl')
my_model.load_state_dict(torch.load('model_param.pkl'))

# モデル全体をpkl化
# ファイルが大きいこと,GPU/CPU情報を含む依存オブジェクトになってしまうことから非推奨
torch.save(model, 'model.pkl')  # save
my_model = torch.load('model.pkl') # load

クランプ関数(0〜255に値を丸める)のやり方

またまたcodewarsの他人の解答より。 本題はRGBの変換だったけど,その前処理で0以下の数値を0に,255以上の数値を255にクランプする処理がある。

僕は富豪的に下記で。

    lst = map(lambda x: 0 if x<0 else x, lst)
    lst = map(lambda x: 255 if x>255 else x, lst)

でも,一行で書くやり方があって,他の人はみんなこれを使ってた。よくやる処理で当たり前なのかもしれない。

clamp = lambda x: min(255, max(x, 0))

文字列からの数値抜き出しとソート時のインデックス取得

CodeWarsより。 わかりやすいように少し改題。また,CodeWarsはPython2で動いているので下記のコードは通らない。

問題
入力:数値を含む文字列の配列が与えられる。ただし,各文字列には1~9の数字が1つだけ入っており,文字列間に含まれる数値の重複はない。
   例) sentence = ['hoge8hoge', 'aa2bed', '5foobar']
出力:文字列に含まれる数値順にソートせよ。
   例) sentence' = ['aa2bed', '5foobar', 'hoge8hoge']

練習になった点は,(1)文字列から数値部を抜き出すには?,(2)ソートした時に,そのインデックスは取得できる? (2)に関して,numpyだとargsortで簡単にできる。sortした時にそのargも返してくれれば良いけど,それは無いので,両方欲しいときはargsortをもとのnumpy配列にインデックスとして渡す。 matlabとかはmax演算の時にargmaxもくれるけど,その違いに似ている。でも,numpyもCとかに比べれば相当楽だ。Pythonは便利だなぁ。

文字列の抜き出し = re.subを使う。味噌はsubは抽出はできずに代替(substitute)するので,数値以外の部分を削る。

import re

string = 'hoge3'
re.sub(re.compile('\D'), '', string)

# 配列に適用したいならmap
map(lambda x: re.sub(re.compile('\D'), '', x), sentence)
imoprt numpy as np

d = [1, 3, 5, 2]
sorted_index = np.argsort(d)  # [0, 3, 1, 2]
sorted = np.array(d)[sorted_index]  # もしソートしたリストも欲しいなら

配列の中に奇数回出現する1つの整数値

codewarsというので遊んでいたら,他人の良い回答を見つけた。

問題
 整数の配列が与えられる。その中に1つだけ奇数回出現する整数値が存在する。それを求めよ。
 例)[1,3,5,2,6,8,4,3,1,5,2,6,8,3] -> 3

色々とやり方はあると思うけれど,XORを使う方法がなるほどと思った。 対象の整数以外は全て偶数回出現する,というのを利用して,XORとればそれは0になる。 reduceはpython3からfunctoolsに入ったみたい。map/fileterは標準なのに変な感じする。

from operator import xor
from functools import reduce
seq = [1,3,5,2,6,8,4,3,1,5,2,6,8,3]
return reduce(xor, seq)