MLMultiArry를 겨냥한 Unsa feMutable RawPoint 처리

5619 단어 SwiftcoreML

MLMultiarray에 대한 정보가 매우 적음


그래서 나는 비망록을 겸하여 배운 것을 기록하고 싶다.

원래 MLMultiArry는


coreML의 데이터를 xcode에 읽은 후 모델의 Input으로 지정한 유형이 MLMultiaArry입니다.이미지 등은 특수 예제에서 다른 유형으로 입력할 수 있지만 일반 수치로 입력할 때는 이 옵션을 사용합니다.
큰 이름이 있지만 말하자면 형식적인 배열일 뿐이다.뒷면에서는 지정된 유형의 크기에 따라 메모리에 순서대로 크기를 확보하는 이른바 C배열의 생각은 문제없다.

MLMultiarray의 기본


이 반의 안배를 전혀 모르는 사람에게, 예를 들면 numby가 말한
variable[0,0,0] = 10
이렇게 대입만 하거나 가치를 얻더라도 어떻게 하면 좋을지 고전해야 한다.(했다.
인터넷에 정보가 별로 없죠...
MLMultiArry는 이렇게 합니다.
variable[[0,0,0]] = 10
좀 더 간단하게 쓰면...
let index = [NSNumber(value: 0), NSNumber(value: 0), NSNumber(value: 0)]
variable[index] = 10
이렇게즉 색인에 전차원 색인을 가진 배열을 전달함으로써 참고할 수 있다.

차원 순서


MLMultiarray와 직접적인 관계는 없지만 코어ML에서 이미지를 처리할 때 (채널, 높이, 너비) 순서대로 처리합니다.따라서 Kerras(Tensorflow)(높이, 너비, 채널) 형식으로 처리하면coremltools에서convert가 바뀔 수 있으므로 MLMultiarray를 사용하여 입력을 만들 때 주의해야 한다.
실제로 약간의 어폐가 있다. 이미지를 처리할 때라기보다는 3차원의 배열이 무단으로 이미지로 처리되기 때문에 아무래도 차원이 바뀐다.
이미지라면 높이, 너비, 채널 수가 거의 일치하지 않아 오류가 발생하지만, 10x10x10의 입방체처럼 수치를 입력하는 모델이라면, 모르면 코어ML과 Keeras (Tensorflow) 에 완전히 다른 입력을 입력합니다.결과가 완전히 달라질 수 있으니 주의가 필요하다.

MLMultiarray의 변형


예를 들어numby에서 데이터를 입력하는 데 공을 들이고 싶을 때 조작은 매우 간단하다.예를 들어 4x5x3(h, w, c)의 이미지를 8x5x3로 세로 연결한다고 가정하면
a = np.zeros(shape=(4,5,3))
b = np.zeros(shape=(4,5,3))

concatenated = np.concatenate((a, b), axis=0)
이렇게 하면 간단하게 쓸 수 있다.가로로 연결하거나 채널 차원으로 연결하는 것은 concentence의 axis만 조정합니다.
그럼 어떻게 하면 Swift에서 MLMultiarray와 같은 일을 할 수 있을까요?제작된 모델이 응용 프로그램에서 실행된다면 스wift에서 같은 처리를 피할 수 없다.
하지만 현재 MLMultiarray는 np입니다.concentence와 같은 편리한 함수는 없습니다.따라서 지침을 이해한 토대에서 메모리에concentence 후의 크기 배열을 확보한 후 그곳으로 복제하는 작업이 필요하다.
(지침을 무시하고 요소 하나하나를 순환으로 대체할 수 있지만 크기가 커지면 순환수가 커지기 때문에 추천하지 않습니다.)
위에 numby가 했던 거랑 똑같은 걸 해야 된다고 생각하니까.
// 1次元目がチャンネル次元になっている事に注意
let a = try! MLMultiArray(shape: [3, 4, 5], dataType: .double)
let b = try! MLMultiArray(shape: [3, 4, 5], dataType: .double)

// メモリを確保
let resultArray = try! MLMultiArray(shape: [3, 8, 5], dataType: .double)

// UnsafeMutableRawPointerだと型が付いておらずassignできないのでbindMemoryで型付け
let resultArrayPointer = resultArray.dataPointer.bindMemory(to: Double.self, capacity: 3*8*5)

// assignで値をコピー
for i in 0..<8 {
    resultArrayPointer.advanced(by: 8*5*i).assign(from: a.dataPointer.bindMemory(to: Double.self, capacity: 3*4*5).advanced(by: 4*5*i), count: 4*5)
    resultArrayPointer.advanced(by: 8*5*i + (4*5)).assign(from: b.dataPointer.bindMemory(to: Double.self, capacity: 3*4*5).advanced(by: 4*5*i), count: 4*5)
}
이렇게 된 느낌.
아마 많은 사람들이 마지막assignで値をコピー이 무엇을 하고 있는지 전혀 모를 거예요. 제가 설명해 드릴게요.(by와count는 곱셈으로 각 차원수를 표시하기 때문에 Pointer 인상을 가진 사람은 조금만 생각해 보면 알 수 있다.)

UnsafeMutable Point를 사용한 assign 이미지


※ 여기서부터 자신의 마음에 있는'인상'으로 작성해 이해하기 쉽도록 한다.따라서 하드웨어의 정의 등에 비해 미묘한 차이가 있을 수 있으니 양해해 주십시오.
C의 지침은 1부터 설명하면 길어지기 때문에 먼저 기본적인 개념만 설명한다.포인터란 메모리의 주소 변수를 가리킨다. 예를 들어 메모리의 100호를 가리키면 안에 100개의 숫자가 있다고 생각하라.
따라서 위의 예resultArrayPointer에는'X호지에 데이터가 있습니다'라는 정보가 있고 코드는'X호지에서 더블 사이즈로capacity 개 데이터에 들어가는 프레임워크가 있습니다'라는 정보를 나타낸다.
간단하다
X = 100
더블 사이즈 = 8
capacity = 20
이렇게 고려하면 100호부터 8호까지 20개의 틀을 확보했다.이번에 준비한 구역이 100-259호라는 뜻이다.(엄밀히 말하면 다른 정보를 보관하는 공백이 있지만 이번에는 고려하지 않는다.)
그리고 더블의 배열을 고려해 수치를 얻으려면 100번, 108번, 116번,
여기서 "3x4x5 같은 복수차원의 배열은 어떻게 들어갑니까?"이런 의문을 가진 사람은 매우 예민하다.
실제로 몇 차원의 데이터라도 메모리에 일렬로 배열된다.그렇다면 어떤 순서로 수치를 넣었을까, 다음은 MLMultiaArry의 화제다.C와 Swift의 다른 비슷한 학급은 어떻게 될지 주의해 주십시오.(나는 기본적으로 같다고 생각한다.)
numby를 잘 쓰는 사람에게는'np.flaten이 만든 순서'를 설명하면 한꺼번에 끝낼 수 있다.이렇게 되면 모르는 사람은 아래의 설명을 보는 토대에서 다양한 3차원 배열을 만들어 numby로 실험할 수 있다.
말로 설명하기는 어렵지만 고차원부터 순서대로 번호를 매긴다고 생각하면 이해하기 쉽다.다음은 3D 상황의 예입니다.
let a = try! MLMultiArray(shape: [3, 4, 5], dataType: .double)

a[[0,0,0]] ・・・1番
a[[0,0,1]] ・・・2番
a[[0,0,2]] ・・・3番
a[[0,0,3]] ・・・4番
a[[0,0,4]] ・・・5番
a[[0,1,0]] ・・・6番
a[[0,1,1]] ・・・7番
a[[0,1,2]] ・・・8番
a[[0,1,3]] ・・・9番
...
a[[0,3,3]] ・・・19番
a[[0,3,4]] ・・・20番
a[[1,0,0]] ・・・21番
...
a[[2,3,4]] ・・・60番
고차원(뒤의 차원)부터 순차적으로 증가한다.
이 점을 이해한 토대에서 numby가 말한'세로 연결 이미지'를 실현하려면 MLMultiArrray가 주소가 말한 곳부터 순서대로 매립하는 것이 아니라는 것을 알게 될 것이다.
'종'의 차원은 2차원이고, a의 21번째로 들어가야 할 곳은 resultArrayPointer의 41번째이기 때문이다.(Index의 경우a의 20은 resultArrayPointer의 40이다.)
이런 것들에 따른 처리를 언어로 쓰면...
· resultArrayPointer의 41개advanced(by: 8*5)에서 더블 사이즈에 따라 41개의 바늘을 가리키는 반환)을 기점a으로 하는 21개~40번째 assign
· resultArrayPointer의 61번째 기점b의 21번째~40번째 assign
그렇습니다.물론 이것은 순환의 두 번째 처리이기 때문에 실제 코드에서 i를 사용하여 각 순환에 적당한 위치를 가진assign을 사용한다.
이로써 최종적으로 2차원ab을 연결하는 MLMultiArry가 완성됐다.
물론 채널 차원에서 결합하고 3차원에서 결합하려면 이 개념을 이해한 토대에서 처리 방법을 바꿔야 한다.(numby의 axis 지정이 얼마나 편리한가

총결산


당돌하지만 다시 쓰면 혼란스러운 사람이 더 혼란스러워지고 이해하는 사람이 이해하니까 정리해보자.
  • MLMultiArry에서 이미지가 말하는 채널 차원은 첫 번째 차원이므로 주의해야 합니다!
  • MLMultiArray.DataPointer는 Unnsafe Mutable RawPointer(무형 포인터)입니다. 돌리기 위해서는 bindMemory를 사용하여 모델을 진행하세요!
  • pointer가 지정한 값의 일렬 배열 방법을 파악합니다!
  • assing원과 assign을 먼저 계산하는 것을 잘못하면 확보하지 못한 영역에 쓰이기 때문에 주의해야 합니다!【중요】
  • 좋은 웹페이지 즐겨찾기